【问题标题】:Bit field larger than 64 shifts in Swift?Swift中大于64移位的位域?
【发布时间】:2016-07-10 22:55:29
【问题描述】:

如何构造一个原始值大于 64 位移位(即Int64)的OptionSetType,它仍然可以使用NSCoder 进行编码?我有超过 64 个潜在的按位选项可以组合。

【问题讨论】:

    标签: swift bit-fields int64 optionsettype


    【解决方案1】:

    所以我最终不得不创建自己的原始struct,这让我很头疼,因为@appzYourLife 提供的库实际上并不能满足UnsignedIntegerTypes 所需的所有协议。以下是我写的一个扩展,实际上允许我写类似

    的东西
    let a: UInt256 = 30
    let b: UInt256 = 1 << 98
    print(a + b)
    

    将输出到控制台:

    0x00000000:00000000:00000000:00000000:00000004:00000000:00000000:0000001E
    

    扩展很长,除了1 之外,还没有实现乘法和除法或位移数字。该版本还支持使用NSCoder进行编码

    //
    //  UInt256.swift
    //  NoodleKit
    //
    //  Created by NoodleOfDeath on 7/10/16.
    //  Copyright © 2016 NoodleOfDeath. All rights reserved.
    //
    
    import Foundation
    
    // Bit Shifting only supports lhs = 1
    
    @warn_unused_result
    public func << (lhs: UInt256, rhs: UInt256) -> UInt256 {
        if lhs > 1 { print("Warning: Only supports binary bitshifts (i.e. 1 << n, where n < 256. Shifting any other numbers than 1 may result in unexpected behavior.") }
        if rhs > 255 { fatalError("shift amount is larger than type size in bits") }
        let shift = UInt64(rhs.parts[7]) % 32
        let offset = Int(rhs.parts[7] / 32)
        var parts = [UInt32]()
        for i in (0 ..< 8) {
            let part: UInt64 = (i + offset < 8 ? UInt64(lhs.parts[i + offset]) : 0)
            let sum32 = UInt32(part << shift)
            parts.append(sum32)
        }
        return UInt256(parts)
    }
    
    @warn_unused_result
    public func >> (lhs: UInt256, rhs: UInt256) -> UInt256 {
        if lhs > 1 { print("Warning: Only supports binary bitshifts (i.e. 1 << n, where n < 256. Shifting any other numbers than 1 may result in unexpected behavior.") }
        if rhs > 255 { fatalError("shift amount is larger than type size in bits") }
        let shift = UInt64(rhs.parts[7]) % 32
        let offset = Int(rhs.parts[7] / 32)
        var parts = [UInt32]()
        for i in (0 ..< 8) {
            let part: UInt64 = (i - offset > 0 ? UInt64(lhs.parts[i - offset]) : 0)
            let sum32 = UInt32(part >> shift)
            parts.append(sum32)
        }
        return UInt256(parts)
    }
    
    @warn_unused_result
    public func == (lhs: UInt256, rhs: UInt256) -> Bool {
        return lhs.parts == rhs.parts
    }
    
    @warn_unused_result
    public func < (lhs: UInt256, rhs: UInt256) -> Bool {
        for i in 0 ..< 8 {
            guard lhs.parts[i] < rhs.parts[i] else { continue }
            return true
        }
        return false
    }
    
    @warn_unused_result
    public func > (lhs: UInt256, rhs: UInt256) -> Bool {
        for i in 0 ..< 8 {
            guard lhs.parts[i] > rhs.parts[i]  else { continue }
            return true
        }
        return false
    }
    
    @warn_unused_result
    public func <= (lhs: UInt256, rhs: UInt256) -> Bool {
        return lhs < rhs || lhs == rhs
    }
    
    @warn_unused_result
    public func >= (lhs: UInt256, rhs: UInt256) -> Bool {
        return lhs > rhs || lhs == rhs
    }
    
    /// Adds `lhs` and `rhs`, returning the result and trapping in case of
    /// arithmetic overflow (except in -Ounchecked builds).
    @warn_unused_result
    public func + (lhs: UInt256, rhs: UInt256) -> UInt256 {
        var parts = [UInt32]()
        var carry = false
        for i in (0 ..< 8).reverse() {
            let lpart = UInt64(lhs.parts[i])
            let rpart = UInt64(rhs.parts[i])
            let comp = lpart == UInt64(UInt32.max) && rpart == UInt64(UInt32.max)
            let sum64 = lpart + rpart + (carry || comp ? 1 : 0)
            let sum32 = UInt32((sum64 << 32) >> 32)
            carry = sum64 > UInt64(UInt32.max)
            parts.insert(sum32, atIndex: 0)
        }
        return UInt256(parts)
    }
    
    /// Adds `lhs` and `rhs`, returning the result and trapping in case of
    /// arithmetic overflow (except in -Ounchecked builds).
    public func += (inout lhs: UInt256, rhs: UInt256) {
        lhs = lhs + rhs
    }
    
    /// Subtracts `lhs` and `rhs`, returning the result and trapping in case of
    /// arithmetic overflow (except in -Ounchecked builds).
    @warn_unused_result
    public func - (lhs: UInt256, rhs: UInt256) -> UInt256 {
        var parts = [UInt32]()
        var borrow = false
        var gave = false
        for i in (0 ..< 8).reverse() {
            borrow = lhs.parts[i] < rhs.parts[i]
            let lpart = UInt64(lhs.parts[i]) - (gave ? 1 : 0) + (borrow ? UInt64(UInt32.max) : 0)
            let rpart = UInt64(rhs.parts[i])
            let sum64 = lpart - rpart
            let sum32 = UInt32((sum64 << 32) >> 32)
            gave = borrow
            parts.insert(sum32, atIndex: 0)
        }
        return UInt256(parts)
    }
    
    public func -= (inout lhs: UInt256, rhs: UInt256) {
        lhs = lhs - rhs
    }
    
    /// Multiplies `lhs` and `rhs`, returning the result and trapping in case of
    /// arithmetic overflow (except in -Ounchecked builds).
    /// - Complexity:  O(64)
    @warn_unused_result
    public func * (lhs: UInt256, rhs: UInt256) -> UInt256 {
        // TODO: - Not Implemented
        return UInt256()
    }
    
    public func *= (inout lhs: UInt256, rhs: UInt256) {
        lhs = lhs * rhs
    }
    
    /// Divides `lhs` and `rhs`, returning the result and trapping in case of
    /// arithmetic overflow (except in -Ounchecked builds).
    @warn_unused_result
    public func / (lhs: UInt256, rhs: UInt256) -> UInt256 {
        // TODO: - Not Implemented
        return UInt256()
    }
    
    public func /= (inout lhs: UInt256, rhs: UInt256) {
        lhs = lhs / rhs
    }
    
    /// Divides `lhs` and `rhs`, returning the remainder and trapping in case of
    /// arithmetic overflow (except in -Ounchecked builds).
    @warn_unused_result
    public func % (lhs: UInt256, rhs: UInt256) -> UInt256 {
        // TODO: - Not Implemented
        return UInt256()
    }
    
    public func %= (inout lhs: UInt256, rhs: UInt256) {
        lhs = lhs % rhs
    }
    
    public extension UInt256 {
    
        @warn_unused_result
        public func toIntMax() -> IntMax {
            return Int64(parts[6] << 32) + Int64(parts[7])
        }
    
        @warn_unused_result
        public func toUIntMax() -> UIntMax {
            return UInt64(parts[6] << 32) + UInt64(parts[7])
        }
    
        /// Adds `lhs` and `rhs`, returning the result and a `Bool` that is
        /// `true` iff the operation caused an arithmetic overflow.
        public static func addWithOverflow(lhs: UInt256, _ rhs: UInt256) -> (UInt256, overflow: Bool) {
            var parts = [UInt32]()
            var carry = false
            for i in (0 ..< 8).reverse() {
                let lpart = UInt64(lhs.parts[i])
                let rpart = UInt64(rhs.parts[i])
                let comp = lpart == UInt64(UInt32.max) && rpart == UInt64(UInt32.max)
                let sum64 = lpart + rpart + (carry || comp ? 1 : 0)
                let sum32 = UInt32((sum64 << 32) >> 32)
                carry = sum64 > UInt64(UInt32.max)
                parts.insert(sum32, atIndex: 0)
            }
            return (UInt256(parts), parts[0] > 0x8fffffff)
        }
    
        /// Subtracts `lhs` and `rhs`, returning the result and a `Bool` that is
        /// `true` iff the operation caused an arithmetic overflow.
        public static func subtractWithOverflow(lhs: UInt256, _ rhs: UInt256) -> (UInt256, overflow: Bool) {
            // TODO: -
            var parts = [UInt32]()
            var borrow = false
            var gave = false
            for i in (0 ..< 8).reverse() {
                borrow = lhs.parts[i] < rhs.parts[i]
                let lpart = UInt64(lhs.parts[i]) - (gave ? 1 : 0) + (borrow ? UInt64(UInt32.max) : 0)
                let rpart = UInt64(rhs.parts[i])
                let sum64 = lpart - rpart
                let sum32 = UInt32((sum64 << 32) >> 32)
                gave = borrow
                parts.insert(sum32, atIndex: 0)
            }
            return (UInt256(parts), parts[0] > 0x8fffffff)
        }
    
        /// Multiplies `lhs` and `rhs`, returning the result and a `Bool` that is
        /// `true` iff the operation caused an arithmetic overflow.
        public static func multiplyWithOverflow(lhs: UInt256, _ rhs: UInt256) -> (UInt256, overflow: Bool) {
            // TODO: - Not Implemented
            return (UInt256(), false)
        }
    
        /// Divides `lhs` and `rhs`, returning the result and a `Bool` that is
        /// `true` iff the operation caused an arithmetic overflow.
        public static func divideWithOverflow(lhs: UInt256, _ rhs: UInt256) -> (UInt256, overflow: Bool) {
            // TODO: - Not Implemented
           return (UInt256(), false)
        }
    
        /// Divides `lhs` and `rhs`, returning the remainder and a `Bool` that is
        /// `true` iff the operation caused an arithmetic overflow.
        public static func remainderWithOverflow(lhs: UInt256, _ rhs: UInt256) -> (UInt256, overflow: Bool) {
            // TODO: - Not Implemented
            return (UInt256(), false)
        }
    
    }
    
    public struct UInt256 : UnsignedIntegerType, Comparable, Equatable {
    
        public typealias IntegerLiteralType = UInt256
        public typealias Distance = Int32
        public typealias Stride = Int32
    
        private let parts: [UInt32]
    
        private var part0: UInt32 { return parts[0] }
        private var part1: UInt32 { return parts[1] }
        private var part2: UInt32 { return parts[2] }
        private var part3: UInt32 { return parts[3] }
        private var part4: UInt32 { return parts[4] }
        private var part5: UInt32 { return parts[5] }
        private var part6: UInt32 { return parts[6] }
        private var part7: UInt32 { return parts[7] }
    
        public static var max: UInt256 {
            return UInt256([.max, .max, .max, .max, .max, .max, .max, .max])
        }
    
        public var description: String {
            var hex = "0x"
            for i in 0 ..< parts.count {
                let part = parts[i]
                hex += String(format:"%08X", part)
                if i + 1 < parts.count {
                    hex += ":"
                }
            }
            return "\(hex)"
        }
    
        public var componentDescription: String {
            return "\(parts)"
        }
    
        public var hashValue: Int {
            return (part0.hashValue + part1.hashValue + part2.hashValue + part3.hashValue + part4.hashValue + part5.hashValue + part6.hashValue + part7.hashValue).hashValue
        }
    
        public var data: NSData {
            let bytes = [part0, part1, part2, part3, part4, part5, part6, part7]
            return NSData(bytes: bytes, length: 32)
        }
    
        public init(_builtinIntegerLiteral builtinIntegerLiteral: _MaxBuiltinIntegerType) {
            self.init(UInt64(_builtinIntegerLiteral: builtinIntegerLiteral))
        }
    
        public init() { parts = [0, 0, 0, 0, 0, 0, 0, 0] }
    
        public init(_ newParts: [UInt32]) {
            var zeros = UInt256().parts
            zeros.replaceRange((8 - newParts.count ..< 8), with: newParts)
            parts = zeros
        }
    
        public init(_ v: Int8) {
            self.init(UInt64(v))
        }
    
        public init(_ v: UInt8) {
            self.init(UInt64(v))
        }
    
        public init(_ v: Int16) {
            self.init(UInt64(v))
        }
    
        public init(_ v: UInt16) {
            self.init(UInt64(v))
        }
    
        public init(_ v: Int32) {
            self.init(UInt64(v))
        }
    
        public init(_ v: UInt32) {
            self.init(UInt64(v))
        }
    
        public init(_ v: Int) {
            self.init(UInt64(v))
        }
    
        public init(_ v: UInt) {
            self.init(UInt64(v))
        }
    
        public init(_ v: Int64) {
            self.init(UInt64(v))
        }
    
        public init(_ v: UInt64) {
            self.init([UInt32(v >> 32), UInt32((v << 32) >> 32)])
        }
    
        public init(integerLiteral value: IntegerLiteralType) {
            parts = value.parts
        }
    
        public init?(data: NSData) {
            var parts = [UInt32]()
            let size = sizeof(UInt32)
            for i in 0 ..< 8 {
                var part = UInt32()
                data.getBytes(&part, range: NSMakeRange(i * size, size))
                parts.append(part)
            }
            guard parts.count == 8 else { return nil }
            self.init(parts)
        }
    
        @warn_unused_result
        public func advancedBy(n: Stride) -> UInt256 {
            return self + UInt256(n)
        }
    
        @warn_unused_result
        public func advancedBy(n: Distance, limit: UInt256) -> UInt256 {
            return limit - UInt256(n) > self ? self + UInt256(n) : limit
        }
    
        @warn_unused_result
        public func distanceTo(end: UInt256) -> Distance {
            return end - self
        }
    
        /// Returns the previous consecutive value in a discrete sequence.
        ///
        /// If `UInt256` has a well-defined successor,
        /// `UInt256.successor().predecessor() == UInt256`.  If `UInt256` has a
        /// well-defined predecessor, `UInt256.predecessor().successor() ==
        /// UInt256`.
        ///
        /// - Requires: `UInt256` has a well-defined predecessor.
        @warn_unused_result
        public func predecessor() -> UInt256 {
            return advancedBy(-1)
        }
    
        @warn_unused_result
        public func successor() -> UInt256 {
            return advancedBy(1)
        }
    
    }
    
    extension UInt256 : BitwiseOperationsType {}
    
    /// Returns the intersection of bits set in `lhs` and `rhs`.
    ///
    /// - Complexity: O(1).
    @warn_unused_result
    public func & (lhs: UInt256, rhs: UInt256) -> UInt256 {
        var parts = [UInt32]()
        for i in 0 ..< 8 {
            parts.append(lhs.parts[i] & rhs.parts[i])
        }
        return UInt256(parts)
    }
    /// Returns the union of bits set in `lhs` and `rhs`.
    ///
    /// - Complexity: O(1).
    @warn_unused_result
    public func | (lhs: UInt256, rhs: UInt256) -> UInt256 {
        var parts = [UInt32]()
        for i in 0 ..< 8 {
            parts.append(lhs.parts[i] | rhs.parts[i])
        }
        return UInt256(parts)
    }
    /// Returns the bits that are set in exactly one of `lhs` and `rhs`.
    ///
    /// - Complexity: O(1).
    @warn_unused_result
    public func ^ (lhs: UInt256, rhs: UInt256) -> UInt256 {
        var parts = [UInt32]()
        for i in 0 ..< 8 {
            parts.append(lhs.parts[i] ^ rhs.parts[i])
        }
        return UInt256(parts)
    }
    /// Returns `x ^ ~UInt256.allZeros`.
    ///
    /// - Complexity: O(1).
    @warn_unused_result
    prefix public func ~ (x: UInt256) -> UInt256 {
        return x ^ ~UInt256.allZeros
    }
    
    public extension UInt256 {
    
        public static var allZeros: UInt256 {
            return UInt256()
        }
    
    }
    
    public extension NSCoder {
    
        public func encodeUInt256(unsignedInteger: UInt256, forKey key: String) {
            encodeObject(unsignedInteger.data, forKey: key)
        }
    
        public func decodeUInt256ForKey(key: String) -> UInt256 {
            guard let data = decodeObjectForKey(key) as? NSData else { return UInt256() }
            return UInt256(data: data) ?? UInt256()
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      免责声明:我从未尝试过

      我想你可以建立自己的Int128。 例如。 this library 定义了 UInt256 类型。

      一旦你有了新类型,我猜你可以简单地将它与 OptionSetType 一起使用。

      struct YourOptions : OptionSetType{
          let rawValue : Int128
          init(rawValue:Int128) {
              self.rawValue = rawValue
          }
      }
      

      【讨论】:

      • 是的,事实上我建议建立你自己的类型。或者使用 GitHub 上定义的那个(UInt256)。
      • 我真的希望避免这种解决方案,因为我必须扩展 NSCoder 来处理编码新的 structNSCoding 是面向类的......
      • @NoodleOfDeath:好的,我明白了。你不能简单地将你的选项分成两个独立的OptionSetType(s)吗?
      • 我可以,但这似乎很烦人。我现在在Array 中有一个Strings 集合作为替代选项,但似乎OptionSetType 对内存更友好,并允许我利用OptionSetType 的功能跨度>
      • 所以我不得不制作我自己的UInt256 版本,并且很高兴报告它可以满足我的需要!感谢您的帮助
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-10
      • 1970-01-01
      • 1970-01-01
      • 2014-08-13
      • 1970-01-01
      • 2011-01-20
      • 2015-12-08
      相关资源
      最近更新 更多