Bit Field Larger Than 64 Shifts in Swift

Bit field larger than 64 shifts in Swift?

So I eventually had to create my own primitive struct which was a pain in the ass, since the library @appzYourLife provided does not actually meet every protocol required of UnsignedIntegerTypes. The following is an extension I wrote that actually allows me to write things like

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

which would output to the console:

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

The extension is pretty lengthy and does not yet implement multiplication and devision or bit-shifting numbers other than 1. This version also supports encoding with and 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()
}

}

How to work with bit operations in Swift?

You don't need to pack the all the datasets into the 20-byte array until the very end so keep them in a an Int array of length 14. Easier to work with that way. When you need to send it over to the hardware, convert it to a UInt8 array of length 20:

struct DataPacket {
var dataSets = [Int](count: 14, repeatedValue: 0)

func toCArray() -> [UInt8] {
var result = [UInt8](count: 20, repeatedValue: 0)
var index = 0
var bitsRemaining = 8
var offset = 0

for value in self.dataSets {
offset = 10

while offset >= 0 {
let mask = 1 << offset
let bit = ((value & mask) >> offset) << (bitsRemaining - 1)
result[index] |= UInt8(bit)

offset -= 1
bitsRemaining -= 1
if bitsRemaining == 0 {
index += 1
bitsRemaining = 8
}
}
}

return result
}
}

// Usage:
var packet = DataPacket()
packet.dataSets[0] = 0b11111111111
packet.dataSets[1] = 0b00000000011
// etc...

let arr = packet.toCArray()

There is a lot of shifting operations going on so I can't explain them all. The general ideal is allocate each of those 11-bit dataset into bytes, spilling over to the next byte as necessary.

How to get higher 20 bits from 64-bit integer?

For uint64_t, you can simply use

i >> 44

For signed types or types that might be larger than 64-bits, you'll also need to mask away the higher bits.

(i >> 44) & 0xFFFFF

A smaller-scale example,

4 bits starting at pos 2 of 8.

7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
| ? | ? | j | ? | ? |
+---+---+---+---+---+---+---+---+

>> 2

+---+---+---+---+---+---+---+---+
| * | * | ? | ? | j | * = 0 if unsigned or original bit7 if signed.
+---+---+---+---+---+---+---+---+

& ( 2**4 - 1 )

+---+---+---+---+---+---+---+---+
| 0 | 0 | 0 | 0 | j |
+---+---+---+---+---+---+---+---+

Swift converts C's uint64_t different than it uses its own UInt64 type

This is an update to my earlier answer after reading your updated question and experimenting some more. I believe the problem is an alignment discrepancy between the imported C structure and the one you manually implemented in Swift. The problem can be solved by using a C helper function to get an instance of the C struct from void pointer as was suggested yesterday, which can then be converted to the manually implemented Swift struct.

I've been able to reproduce the problem after creating an abbreviated mock-up of your DeviceState structure that looks like

typedef struct
{
uint16_t revision;
uint16_t client;
uint16_t cmd;
int16_t parameter;
int32_t value;
uint64_t time;
uint8_t stats[8];
uint16_t compoundValueOld;
} APIStruct;

The corresponding hand-crafted Swift native structure is:

struct MyStruct
{
init( _apis : APIStruct)
{
revision = _apis.revision
client = _apis.client
cmd = _apis.cmd
parameter = _apis.parameter
value = _apis.value
time = _apis.time
stats = _apis.stats
compoundValueOld = _apis.compoundValueOld
}

var revision : UInt16
var client : UInt16
var cmd : UInt16
var parameter : Int16
var value : Int32
var time : UInt64
var stats : (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8);
var compoundValueOld : UInt16
}

The C framework you are working with could have been compiled using a different struct packing, resulting in a non-matching alignment. I used

#pragma pack(2) 

in my C code to break the bit-matching between the Swift's native and imported C struct.

If I do something like

func swiftCallBackVoid( p: UnsafeMutablePointer<Void> )
{
...
let _locMS:MyStruct = (UnsafeMutablePointer<MyStruct>(p)).memory
...
}

the data in _locMS is different from what was placed there by C code. This problem only occurs if I change struct packing using a pragma in my C code; the above unsafe conversion works fine if the default alignment is used. One can solve this problem as follows:

let _locMS:MyStruct = MyStruct(_apis: (UnsafeMutablePointer<APIStruct>(p)).memory)

BTW, the way Swift imports the C struct, the array members become tuples; this can be seen from the fact that tuple notation has to be used to access them in Swift.

I have a sample Xcode project illustrating all this that I've placed on github:

https://github.com/omniprog/xcode-samples

Obviously, the approach of using a helper C function to get APIStruct from a void pointer and then converting the APIStruct to MyStruct may or may not be an option, depending on how the structures are used, how large they are, and on the performance requirements of the application. As you can tell, this approach involves some copying of the structure. Other approaches, I think, include writing a C-layer between Swift code and the 3rd party C framework, studying the memory layout of the C structure and accessing it in creative ways (may break easily), using the imported C struct more extensively in your Swift code, etc...

Here is a way to share data between C and Swift code without unnecessary copying and with changes made in Swift visible to C code. With the following approach, however, it's imperative to be aware of object lifetime and other memory management issues. One can create a class as follows:

// This typealias isn't really necessary, just a convenience
typealias APIStructPtr = UnsafeMutablePointer<APIStruct>

struct MyStructUnsafe
{
init( _p : APIStructPtr )
{
pAPIStruct = _p
}

var time: UInt64 {
get {
return pAPIStruct.memory.time
}
set( newVal ) {
pAPIStruct.memory.time = newVal
}
}
var pAPIStruct: APIStructPtr
}

Then we can use this structure as follows:

func swiftCallBackVoid( p: UnsafeMutablePointer<Void> )
{
...
var _myUnsafe : MyStructUnsafe = MyStructUnsafe(_p: APIStructPtr(p))
...
_myUnsafe.time = 9876543210 // this change is visible in C code!
...
}

Why does declaring a combination of bit fields inside an enum produce a different result than declaring it outside of the enum?

You're not distinguishing between enum values and variables, but these are very different.


Enum abuse

As an aside, I think you're abusing the purpose of an enum by trying to sneak some extra metadata about these enumvalues (i.e. whether they are optional or not) into the composed Optional field.

I suspect the best solution for you is to move away from using the enum entirely, since enum values shouldn't have more metada surrounding them.

I have still answered the question as my suspicion of enum abuse is solely based on a name and my interpretation of its meaning to you. It's up to you to decide whether you're trying to sneak some metadata in the enum or whether I misunderstood your intention.


Enum values

[Flags]
enum Subjects
{
Art = 0b_0000_0001,
Agriculture = 0b_0000_0010,
Optional = Art | Agriculture,
}

When you include the composed value in the enum, you define it as a valid enum value. You are literally telling the compiler that Subjects.Optional is a valid (and thus meaningful) value of the enum, implying that this can and should be used.

That leads the compiler to use the Subjects.Optional value (and its string representation, i.e. "Optional") because you told the compiler that it's meaningful to you.

Variables

[Flags]
enum Subjects
{
Art = 0b_0000_0001,
Agriculture = 0b_0000_0010
}

var optional = Subjects.Art | Subjects.Agriculture;

It's important to realize there that optional is a variable and not an enum value. There are only two enum values here, Art and Agriculture.

In this case, you did not define Optional to be an enum value, and therefore the compiler cannot use or refer to an enum value that doesn't exist.

Therefore, it falls back on figuring out which combination of enum values would result in the (combined) optional value, and it realizes that by combining Subject.Art and Subject.Agriculture, you get the value described by optional, which is why it returns a comma-separated string Art, Agriculture.


If you want to get the comma-separated string while also retaining the composed values in the enum itself, you're going to have to generate the comma-separated string yourself. For example:

public string AsCommaSeparatedString(Subjects myEnum)
{
var possibleSubjects = new List<Subjects>() { Subjects.Art, Subjects.Agriculture };

var subjects = possibleSubjects.Where(possibleSubject => myEnum.HasFlag(possibleSubject));

return String.Join(",", names);
}

You'll have to list all the enums values which you want to include (so others like Optional will be ignored), but that's unavoidable when you specifically want to exclude some values (like Optional) from being mentioned.

Bitboard 64-bit machine: should I use int16 or int64 for a 4x4 board?

  • If you are looking to save space, use uint16_t.
  • If you are looking to save time, use uint_fast16_t.
  • uint64_t may be useful, too, for making an array of values aligned at 8-byte boundaries. This is unlikely to give you much benefits, though, because it comes at the price of wasting 75% of memory allocated for the array, along with the associated loss in cache performance.

Note: you may end up using the same type as uint64_t if your library maps uint_fast16_t to uint64_t.



Related Topics



Leave a reply



Submit