Created
May 1, 2023 10:14
-
-
Save SintraWorks/40a52d5f299d10040e73b2ad4dcc1355 to your computer and use it in GitHub Desktop.
A BitField provides storage for a set a flags representing bits
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 Foundation | |
/// A `BitField` is a struct that provides storage for a set a flags, | |
/// where each flag represents a bit either being set or not set (clear). | |
/// | |
/// `BitField` is closely related to Swift's `OptionSet`, but it specializes | |
/// on treating bits as bits, rather than as abstract flags. | |
public struct BitField<T: UnsignedInteger> { | |
/// `naturalSize` represents the maximum number of bits that a `BitField` can hold, depending on the | |
/// size of the type over which it is generic. | |
/// | |
/// A `BitField<UInt8>` has a `naturalSize` of `8`. | |
/// | |
/// A `BitField<UInt16>` has a `naturalSize` of `16`. | |
/// | |
/// A `BitField<UInt32>` has a `naturalSize` of `32`. | |
/// | |
/// A `BitField<UInt>` has a `naturalSize` of `32` or `64` depending on (hardware) platform. | |
/// | |
/// A `BitField<UInt64>` has a `naturalSize` of `64`. | |
/// | |
public static var naturalSize: Int { MemoryLayout<T>.size * 8 } | |
/// A BitField's `size` represents the number of bits in the BitField. | |
/// Setting a size larger than the `naturalSize` is not allowed. | |
/// | |
/// If you try to set a size larger than the `naturalSize`, the `size` will be the `naturalSize`. | |
public var size: Int = Self.naturalSize { | |
didSet { | |
if size > Self.naturalSize { | |
size = Self.naturalSize | |
} | |
} | |
} | |
/// The `rawValue` is the numeric value that represents the sum of the values of the bits that are set. | |
public var rawValue: T = 0 | |
/// The default initializer creates an empty `BitField` if no `rawValue` is provided. | |
/// It also defaults the size of the` BitField` to its natural size. | |
public init(rawValue: T = 0) { | |
size = Self.naturalSize | |
self.rawValue = rawValue | |
} | |
/// You can instantiate a `BitField` by optionally providing a raw value, and a size | |
/// that is smaller than, or equal to, its natural size. | |
/// The rawValue that is passed in must not be greater than the amount the bits can represent. | |
public init?(rawValue: T = 0, size: Int) { | |
guard size > 0, size <= Self.naturalSize, | |
rawValue < Int(pow(Double(2), Double(size))) | |
else { return nil } | |
self.size = size | |
self.rawValue = rawValue | |
} | |
/// You can instantiate a `BitField` by providing a variadic set of indexes, and, optionally, a size | |
/// that is smaller than, or equal to, its natural size. | |
/// An index represents a bit in the `BitField`, with index 0 representing the least significant bit. | |
/// The initializer will fail if one or more invalid indexes are supplied. | |
public init?(_ indexes: T..., size: Int = Self.naturalSize) { | |
guard setInitialIndexes(indexes, size: size) | |
else { return nil } | |
} | |
/// You can instantiate a `BitField` by providing an array of indexes, and, optionally, a size | |
/// that is smaller than, or equal to, its natural size. | |
/// An index represents a bit in the `BitField`, with index 0 representing the least significant bit. | |
/// The initializer will fail if one or more invalid indexes are supplied. | |
public init?(_ indexes: [T], size: Int = Self.naturalSize) { | |
guard setInitialIndexes(indexes, size: size) | |
else { return nil } | |
} | |
private mutating func setInitialIndexes(_ indexes: [T], size: Int) -> Bool { | |
guard size > 0, size <= Self.naturalSize | |
else { return false } | |
self.size = size | |
for index in indexes { | |
guard index < size else { return false } | |
rawValue |= (1 << index) | |
} | |
return true | |
} | |
/// Set one or more bits in the `BitField`. | |
/// Invalid indexes are ignored. | |
@discardableResult | |
public mutating func set(_ indexes: T...) -> Self { | |
for index in indexes { | |
guard index < size else { continue } | |
rawValue |= (1 << index) | |
} | |
return self | |
} | |
/// Clear one or more bits in the `BitField`. | |
/// Invalid indexes are ignored. | |
@discardableResult | |
public mutating func clear(_ indexes: T...) -> Self { | |
for index in indexes { | |
guard index < size else { continue } | |
rawValue = rawValue & ~(1 << index) | |
} | |
return self | |
} | |
/// Check whether one or more bits in the `BitField` are set. | |
/// If all bits passed in are set this function returns `true`. | |
/// If at least one of the checked bits is not set, or if any invalid bit indexes | |
/// are provided, this function returns `false`. | |
public func isSet(_ indexes: T...) -> Bool { | |
for index in indexes { | |
guard index < size else { return false } | |
if (rawValue & (1 << index)) == 0 { | |
return false | |
} | |
} | |
return true | |
} | |
func debugDescription() -> String { | |
(0 ..< size).reversed().reduce("0b") { | |
$0 + (isSet(T($1)) ? "1" : "0") | |
} | |
} | |
} | |
extension BitField { | |
/// Initialize a `BitField` from an array of `bool`s. | |
public init(_ bools: [Bool]) { | |
rawValue = 0 | |
for (index, bool) in bools.enumerated() where bool { | |
set(T(index)) | |
} | |
} | |
/// Turn a `BitField` into an array of `bool`s. | |
public func boolArrayOf(size: Int) -> [Bool] { | |
let validSize = min(size, Self.naturalSize) | |
var array = [Bool]() | |
for bitIndex in 0 ..< validSize { | |
array.append(isSet(T(bitIndex))) | |
} | |
return array | |
} | |
/// Create a copy of the `BitField` with the sequence of bits reversed. | |
public func reversed(size: Int? = nil) -> BitField { | |
BitField(boolArrayOf(size: size ?? Self.naturalSize).reversed()) | |
} | |
} | |
extension BitField: Comparable { | |
public static func < (lhs: Self, rhs: Self) -> Bool { | |
lhs.rawValue < rhs.rawValue | |
} | |
} | |
extension BitField { | |
public func contains(_ other: BitField) -> Bool { | |
self & other == other | |
} | |
public static func & (lhs: Self, rhs: Self) -> Self { | |
BitField(rawValue: lhs.rawValue & rhs.rawValue) | |
} | |
public static func | (lhs: Self, rhs: Self) -> Self { | |
BitField(rawValue: lhs.rawValue | rhs.rawValue) | |
} | |
public static func ^ (lhs: Self, rhs: Self) -> Self { | |
BitField(rawValue: lhs.rawValue ^ rhs.rawValue) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment