将代码从Objective-C转换为Swift后,testing用例失败
我正在做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)) }
testing用例:
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)") } }
有人可以指导我为什么第二个testing用例失败,但客观的C版本通过?
编辑:正如@vacawama提到的:如果你把testGetNextSet分解成2个testing,都会通过。
编辑2:当我运行xctool
testing,并testing运行时调用BitArray
的nextSet()
将崩溃。
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
,您将返回值从Int32
更改为Int
。 这很好,但函数的最后一行在翻译时不能正常运行。
在numberOfTrailingZeros
,将其replace为:
return n - Int((UInt((i << 1)) >> 31))
有了这个:
return n - Int(UInt32(bitPattern: i << 1) >> 31)
转换为UInt32
将删除除32位以外的所有内容。 既然你UInt
,你并没有删除这些位。 有必要使用bitPattern
来做到这一点。
最后我发现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 { startIdx.destroy() startIdx.dealloc(bitsLength * sizeof(Int32)) }