5

我正在以 Swift 风格进行一些按位运算,这些代码最初是用 Objective-C/C 编写的。我UnsafeMutablePointer用来声明内存地址的起始索引并UnsafeMutableBufferPointer用于访问范围内的元素。

您可以在此处访问原始的 Objective-C 文件。

public init(size: Int) {
    self.size = size
    self.bitsLength = (size + 31) / 32
    self.startIdx = UnsafeMutablePointer<Int32>.alloc(bitsLength * sizeof(Int32))
    self.bits = UnsafeMutableBufferPointer(start: startIdx, count: bitsLength)
}

/**
 * @param from first bit to check
 * @return index of first bit that is set, starting from the given index, or size if none are set
 *  at or beyond its given index
 */
public func nextSet(from: Int) -> Int {
    if from >= size { return size }
    var bitsOffset = from / 32
    var currentBits: Int32 = bits[bitsOffset]
    currentBits &= ~((1 << (from & 0x1F)) - 1).to32
    while currentBits == 0 {
        if ++bitsOffset == bitsLength {
            return size
        }
        currentBits = bits[bitsOffset]
    }
    let result: Int = bitsOffset * 32 + numberOfTrailingZeros(currentBits).toInt
    return result > size ? size : result
}

func numberOfTrailingZeros(i: Int32) -> Int {
    var i = i
    guard i != 0 else { return 32 }
    var n = 31
    var y: Int32
    y = i << 16
    if y != 0 { n = n - 16; i = y }
    y = i << 8
    if y != 0 { n = n - 8; i = y }
    y = i << 4
    if y != 0 { n = n - 4; i = y }
    y = i << 2
    if y != 0 { n = n - 2; i = y }
    return n - Int((UInt((i << 1)) >> 31))
}

测试用例:

func testGetNextSet1() {
    // Passed
    var bits = BitArray(size: 32)
    for i in 0..<bits.size {
        XCTAssertEqual(32, bits.nextSet(i), "\(i)")
    }
    // Failed
    bits = BitArray(size: 34)
    for i in 0..<bits.size {
        XCTAssertEqual(34, bits.nextSet(i), "\(i)")
    }
}

有人可以指导我为什么第二个测试用例失败但objective-c版本通过吗?

编辑:正如@vacawama 提到的:如果您将 testGetNextSet 分成 2 个测试,则都通过。

Edit2:当我运行测试时xctool,调用BitArray's 的测试nextSet()会在运行时崩溃。

4

2 回答 2

3

Objective-C 版本numberOfTrailingZeros

// Ported from OpenJDK Integer.numberOfTrailingZeros implementation
- (int32_t)numberOfTrailingZeros:(int32_t)i {
    int32_t y;
    if (i == 0) return 32;
    int32_t n = 31;
    y = i <<16; if (y != 0) { n = n -16; i = y; }
    y = i << 8; if (y != 0) { n = n - 8; i = y; }
    y = i << 4; if (y != 0) { n = n - 4; i = y; }
    y = i << 2; if (y != 0) { n = n - 2; i = y; }
    return n - (int32_t)((uint32_t)(i << 1) >> 31);
}

翻译时numberOfTrailingZeros,您将返回值从 更改Int32Int。没关系,但函数的最后一行在您翻译时无法正常运行。

numberOfTrailingZeros中,替换为:

return n - Int((UInt((i << 1)) >> 31))

有了这个:

return n - Int(UInt32(bitPattern: i << 1) >> 31)

转换为UInt32删除除低 32 位之外的所有位。由于您要转换为UInt,因此您并没有删除这些位。有必要使用它bitPattern来实现这一点。

于 2016-04-16T13:48:43.420 回答
0

最后我发现startIdx只需要在分配后进行初始化。

self.startIdx = UnsafeMutablePointer<Int32>.alloc(bitsLength * sizeof(Int32))
self.startIdx.initializeFrom(Array(count: bitsLength, repeatedValue: 0))

calloc仅使用一行代码:

self.startIdx = unsafeBitCast(calloc(bitsLength, sizeof(Int32)), UnsafeMutablePointer<Int32>.self)

此外,我使用lazy var延迟初始化UnsafeMutableBufferPointer直到第一次使用该属性。

lazy var bits: UnsafeMutableBufferPointer<Int32> = {
   return UnsafeMutableBufferPointer<Int32>(start: self.startIdx, count: self.bitsLength)
}()

另一方面,不要忘记deinit

deinit {
    startIdx.destroy()
    startIdx.dealloc(bitsLength * sizeof(Int32))
}
于 2016-05-09T14:14:05.867 回答