-
Notifications
You must be signed in to change notification settings - Fork 3
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
Endianness sensitive collection v2
#72
Comments
Also, rebasing slices is too complicated. I'll have a look at that. Perhaps: typealias SubSequence = NBKEndiannessOrdered<Base.SebSequence, Endianness> |
This model can be made standalone, but endianness justifies the constraints. |
Another thought: there's a light-weight alternative without protocols: struct BigEndianRange { // light-weight without conformances
typealias Base = Range<Int>
let base: Base
init(_ base: Base) { self.base = base }
subscript(index: Int) -> Base.Element {
#if _endian(big)
base[base.index(base.startIndex, offsetBy: index)]
#else
base[base.index(base.endIndex, offsetBy: ~index)]
#endif
}
}
let base = [0, 1, 2, 3]
let bigEndianOrderedIndices = BigEndianRange(base.indices)
for baseIndex in bigEndianOrderedIndices.base {
print(base[bigEndianOrderedIndices[baseIndex]], base[baseIndex])
} |
Another thought: perhaps I don't need to parameterize endianness if I pass an index and a direction to the iterator. If the iterator just calls |
So I had a really neat realization. I can make the dynamic case branchless if the base index is constrained to Int by storing an endianness sensitive base index and a bit-mask used to XOR the offset relative to the base index. Galaxy brain stuff. Edit: oh, wait, there's more. I'm XOR-ing the offset, so the base index can be whatever. Edit: oh, wait, there's more. If I add the opposite index, the collection becomes self-slicing. Edit: Hm. It seems useful beyond this project. I might turn it into a separate mini-package. Edit: self-slicing is made too awkward by the shared indices requirement of subsequences. |
TODO: If I pick a dynamic solution, I can remove potential calls to reverse() without creating more specialization of NBK.integerTextUnchecked(...). The potential reverse() calls come from NBKDoubleWidth/withContiguousStorage(_:). |
The desire to co-opt "endianness" to contextualize the collection's directionality is strong, but I suppose I could call it "TwinHeaded", "BooleanOrdered", "ReversibleCollection" or something else entirely. |
I see two alternatives using this approach: with and without a stored index (in case it matters). @inlinable func baseSubscriptIndex(_ index: Int) -> Base.Index {
return base.index(edge, offsetBy: index ^ mask)
return base.index(base.startIndex, offsetBy: index ^ mask &+ count &* (~mask &+ 1))
} |
Hm. I suppose I cannot expose a mutable base with a store index, but I can without. |
Hm. Slicing and rebasing seems slow. I wonder if I need a custom subsequence model. I can work around it by wrapping the indices, rebasing the buffer, then wrapping the rebased buffer—but that's lame. Edit: I can't think of another collection that allows this kind of nested rebasing, so maybe I should just drop it? |
I can get rebasing to perform with a custom subsequence. I'm unsure why it's better than Slice<Self>. Edit: I think Slice<Self> will work, but you have to avoid |
Here are some really nice conveniences I could think of: /// Returns whether the base collection's elements are presented front-to-back.
var isFrontToBack: Bool { get }
/// Returns the base collection, if its elements matches this collection.
var asFrontToBack: Base? { get }
/// Returns whether the base collection's elements are presented back-to-front.
var isBackToFront: Bool { get }
/// Returns the base collection but reversed, if its elements matches this collection.
var asBackToFront: ReversedCollection<Base>? { get }
/// Creates a collection with the same memory and direction as the given subsequence.
init<T>(rebasing subsequence: SubSequence) where Base == UnsafeBufferPointer<T>
/// Creates a collection with the same memory and direction as the given subsequence.
init<T>(rebasing subsequence: SubSequence) where Base == UnsafeMutableBufferPointer<T> |
I've played with conveniences like |
Zzz. It seems like I'll have to expose |
This is cute; you can get the direction of iteration as a var direction: Int { mask &<< 1 | 1 } |
Here's a neat drop-last-with-rebase trick using pointers and free™ NBKTwinHeaded(rebasing: NBKTwinHeaded(data).reversed().drop(while:{ $0.isZero })).reversed() |
Reworking
NBKLittleEndianOrdered
to also handle big endianness and dynamic ordering is quite simple, and it appears to be just as viable in Swift 5.8 (did not test 5.7). I envision something like the following:I can even make the change seamless with some type aliases:
The text was updated successfully, but these errors were encountered: