Testing out a 4-bit unsigned integer type:
public struct UInt4: FixedWidthInteger, UnsignedInteger {
public init(integerLiteral value: UInt) {
self.init(exactly: value)! //self.init(value)
}
public static func * (lhs: UInt4, rhs: UInt4) -> UInt4 {
let result = lhs.multipliedReportingOverflow(by: rhs)
precondition(!result.overflow)
return result.partialValue
}
public static func *= (lhs: inout UInt4, rhs: UInt4) { lhs = lhs * rhs }
public static func + (lhs: UInt4, rhs: UInt4) -> UInt4 {
let result = lhs.addingReportingOverflow(rhs)
precondition(!result.overflow)
return result.partialValue
}
public static func += (lhs: inout UInt4, rhs: UInt4) { lhs = lhs + rhs }
public static func - (lhs: UInt4, rhs: UInt4) -> UInt4 {
let result = lhs.subtractingReportingOverflow(rhs)
precondition(!result.overflow)
return result.partialValue
}
public static func -= (lhs: inout UInt4, rhs: UInt4) { lhs = lhs - rhs }
public var hashValue: Int { return value.hashValue }
public init<T: BinaryFloatingPoint>(_ source: T) {
fatalError("\(#function) not implemented.")
}
public var words: UInt8.Words { return value.words }
public var trailingZeroBitCount: Int { return Swift.max(value.trailingZeroBitCount, bitWidth) }
public func quotientAndRemainder(dividingBy rhs: UInt4) -> (quotient: UInt4, remainder: UInt4) {
return rhs.dividingFullWidth((high: 0, low: self))
}
public func signum() -> UInt4 { return UInt4(truncatingIfNeeded: value.signum()) }
public static func &= (lhs: inout UInt4, rhs: UInt4) { lhs.value &= rhs.value }
public static func / (lhs: UInt4, rhs: UInt4) -> UInt4 {
let result = lhs.dividedReportingOverflow(by: rhs)
precondition(!result.overflow)
return result.partialValue
}
public static func /= (lhs: inout UInt4, rhs: UInt4) { lhs = lhs / rhs }
public static func % (lhs: UInt4, rhs: UInt4) -> UInt4 {
let result = lhs.remainderReportingOverflow(dividingBy: rhs)
precondition(!result.overflow)
return result.partialValue
}
public static func %= (lhs: inout UInt4, rhs: UInt4) { lhs = lhs % rhs }
public static func ^= (lhs: inout UInt4, rhs: UInt4) { lhs.value ^= rhs.value }
public static func |= (lhs: inout UInt4, rhs: UInt4) { lhs.value |= rhs.value }
public init(_truncatingBits value: UInt) {
self.value = UInt8(truncatingIfNeeded: value) & 0x0F
}
public var nonzeroBitCount: Int { return value.nonzeroBitCount }
public var leadingZeroBitCount: Int { return value.leadingZeroBitCount - 4 }
public var byteSwapped: UInt4 { return self }
public func addingReportingOverflow(_ rhs: UInt4) -> (partialValue: UInt4, overflow: Bool) {
let result = value.addingReportingOverflow(rhs.value)
assert(!result.overflow)
return (UInt4(truncatingIfNeeded: result.partialValue), result.partialValue >> 4 != 0)
}
public func subtractingReportingOverflow(_ rhs: UInt4) -> (partialValue: UInt4, overflow: Bool) {
let result = value.subtractingReportingOverflow(rhs.value)
return (UInt4(truncatingIfNeeded: result.partialValue), result.overflow)
}
public func multipliedReportingOverflow(by rhs: UInt4) -> (partialValue: UInt4, overflow: Bool) {
let result = value.multipliedReportingOverflow(by: rhs.value)
assert(!result.overflow)
return (UInt4(truncatingIfNeeded: result.partialValue), result.partialValue >> 4 != 0)
}
public func dividedReportingOverflow(by rhs: UInt4) -> (partialValue: UInt4, overflow: Bool) {
let result = value.dividedReportingOverflow(by: rhs.value)
return (UInt4(truncatingIfNeeded: result.partialValue), result.overflow)
}
public func remainderReportingOverflow(dividingBy rhs: UInt4) -> (partialValue: UInt4, overflow: Bool) {
let result = value.remainderReportingOverflow(dividingBy: rhs.value)
return (UInt4(truncatingIfNeeded: result.partialValue), result.overflow)
}
public func multipliedFullWidth(by other: UInt4) -> (high: UInt4, low: UInt4) {
let result = value.multipliedFullWidth(by: other.value)
assert(result.high == 0)
return (UInt4(truncatingIfNeeded: result.low >> 4), UInt4(truncatingIfNeeded: result.low))
}
public func dividingFullWidth(_ dividend: (high: UInt4, low: UInt4)) -> (quotient: UInt4, remainder: UInt4) {
let result = value.dividingFullWidth((high: 0, low: dividend.high.value << 4 | dividend.low.value))
precondition(result.quotient & 0xF0 == 0)
return (UInt4(truncatingIfNeeded: result.quotient), UInt4(truncatingIfNeeded: result.remainder))
}
public static var bitWidth: Int { return 4 }
public static func &<<= (lhs: inout UInt4, rhs: UInt4) { lhs.value <<= (rhs.value & 0x03) ; lhs.value &= 0x0F }
public static func &>>= (lhs: inout UInt4, rhs: UInt4) { lhs.value >>= (rhs.value & 0x03) }
/// The raw value.
var value: UInt8
}
With this code, first in a playground, now in a CLI project so I can set breakpoints:
print(UInt8.bitWidth, UInt4.bitWidth)
print(UInt4(_truncatingBits: 22))
print(UInt4(6))
The first two lines work. (That second line prints "6" as it should.) But the third line hangs. In the playground version, it was showing that "bitWidth" was being called thousands of times before giving up with bad-access. Now that I can use breakpoints, the third line goes:
- Calls the
ExpressibleByIntegerLiteral initializer
- Which calls whichever to the two
BinaryInteger initializers I didn't comment out.
- Calls some initializer within
UnsignedInteger (whose code is in assembly).
- Which calls my integer-literal initializer again!
So I got infinite recursion.
Help.