Last active
March 11, 2024 05:11
-
-
Save JadenGeller/2c94515b425ffb86b3e7919f3f58f5c7 to your computer and use it in GitHub Desktop.
useful for buffering models for a scroll view!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// import https://gist.github.com/JadenGeller/66585051f8de54d9f2a3df1acfd87c32#file-pinnedcollection-swift | |
import DequeModule | |
struct AdaptiveRangeBuffer<Index: Strideable, Element>: RandomAccessCollection, MutableCollection where Index.Stride == Int { | |
var storage: PinnedCollection<Deque<Element>, Index> | |
var defaultValue: (Index) -> Element | |
var willRemoveRange: (Slice<Self>) -> Void | |
init(indices: Range<Index>, defaultValue: @escaping (Index) -> Element, willRemoveRange: @escaping (Slice<Self>) -> Void = { _ in }) { | |
self.defaultValue = defaultValue | |
self.willRemoveRange = willRemoveRange | |
self.storage = Deque(indices.lazy.map(defaultValue)).reindex(from: indices.startIndex).pinningIndices() | |
} | |
var startIndex: Index { | |
get { | |
storage.startIndex | |
} | |
set { | |
let distance = startIndex.distance(to: newValue) | |
if distance >= 0 { | |
willRemoveRange(self[startIndex..<newValue]) | |
storage.removeFirst(distance) | |
} else { | |
let newIndices = startIndex.advanced(by: distance)..<startIndex | |
storage.prepend(contentsOf: newIndices.lazy.map(defaultValue)) | |
} | |
} | |
} | |
var endIndex: Index { | |
get { | |
storage.endIndex | |
} | |
set { | |
let distance = endIndex.distance(to: newValue) | |
if distance <= 0 { | |
willRemoveRange(self[newValue..<endIndex]) | |
storage.removeLast(-distance) | |
} else { | |
let newIndices = endIndex..<endIndex.advanced(by: distance) | |
storage.append(contentsOf: newIndices.lazy.map(defaultValue)) | |
} | |
} | |
} | |
var indices: Range<Index> { | |
get { | |
storage.indices | |
} | |
set { | |
guard newValue.overlaps(indices) else { | |
willRemoveRange(self[...]) | |
self = .init(indices: newValue, defaultValue: defaultValue, willRemoveRange: willRemoveRange) | |
return | |
} | |
if newValue.lowerBound > startIndex { // shrink first | |
startIndex = newValue.lowerBound | |
endIndex = newValue.upperBound | |
} else { | |
endIndex = newValue.upperBound | |
startIndex = newValue.lowerBound | |
} | |
} | |
} | |
subscript(position: Index) -> Element { | |
get { | |
storage[position] | |
} | |
set { | |
storage[position] = newValue | |
} | |
} | |
} | |
extension AdaptiveRangeBuffer { | |
mutating func slide(toContain index: Index) { | |
if index < startIndex { | |
let offset = startIndex.distance(to: index) | |
indices = startIndex.advanced(by: offset)..<endIndex.advanced(by: offset) | |
} | |
else if index >= endIndex { | |
// FIXME: Consider migrating to closed rainge to simplify | |
let offset = endIndex.distance(to: index) + 1 // to contain! | |
indices = startIndex.advanced(by: offset)..<endIndex.advanced(by: offset) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment