1

I'm trying to convert an Objective C method to Swift but in order to do so I need some help understanding what's going on.

For context, the input to this method begins as an NSString which is then converted into an NSData object with utf8 encoding. The bytes of this data object are then passed into the method (the message parameter).

The return value of this method is then sent via writeData to a certain characteristic of a CBPeripheral.

Here's what I understand (from a Swift perspective).

  • The message passed in is a UInt8 byte array: [UInt8]
  • 3 variables are created as UInt32 values; crcVal, byteVal and mask
  • crcVal is then set to the max value of UInt32 type
  • each byte from the UInt8 byte array passed in is then looped through and some kind of operation is performed to produce and append to the final outcome of the crcVal value which is ultimately used to send via CoreBluetooth's writeData command to a CBPeripheral.

I'm not very solid on bitwise operators or why the below method is doing what it's doing as there's no documentation. Can somebody help clear up any or all parts of this? I'm hoping to write a Swift equivalent version of this method. Thank you.

- (uint32_t) computeCRC:(uint8_t *)message len:(NSUInteger)msgLen {

    uint32_t crcVal = 0, byteVal, mask;

    crcVal = 0xFFFFFFFF;
    for (int i=0; i<msgLen; i++) {
        byteVal = message[i];
        crcVal ^= byteVal;
        for (int j=7; j>=0; j--) {
            mask = -(crcVal&1);
            crcVal = (crcVal>>1)^(0xEDB88320 & mask);
        }
    }

    return ~crcVal;
}
Sulthan
  • 128,090
  • 22
  • 218
  • 270
Aaron
  • 6,466
  • 7
  • 37
  • 75

2 Answers2

4

Instead of writing the implementation from scratch, why don't you use the zlib implementation of crc32?:

import zlib

func crc32(from data: Data) -> UInt {
    return data.withUnsafeBytes { (buffer: UnsafePointer<Bytef>) -> UInt in
       return zlib.crc32(0, buffer, uInt(data.count))
    }
}

However, to help you understand the bitwise operations:

func computeCRC(message: [UInt8]) -> UInt32 {
    var crc: UInt32 = 0xFFFFFFFF

    for byte in message {
        crc ^= UInt32(byte)
        for _ in 0 ..< 8 {
            // negation using 2-complement: -x = ~x + 1
            // use &+ addition with overflow
            let mask = ~(crc & 1) &+ 1
            crc = (crc >> 1) ^ (0xEDB88320 & mask)
        }
    }

    return ~crc
}

Note that we don't need to pass the number of bytes in Swift. The implementations have different signatures and return a slightly different type but they both give the same result.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • I was unaware that CRC was commonplace. I thought it was some proprietary cryptic method. Now that I know that it comes baked in I will definitely use this. Thanks! – Aaron Jun 02 '18 at 18:17
1

For calculation CRC16 you can use

import Foundation

extension Data {

    enum CRCType {
        case MODBUS
        case ARC
    }

    static func crc16(_ data: [UInt8], type: CRCType) -> UInt16? {
        if data.isEmpty {
            return nil
        }
        let polynomial: UInt16 = 0xA001 // A001 is the bit reverse of 8005
        var accumulator: UInt16
        // set the accumulator initial value based on CRC type
        if type == .ARC {
            accumulator = 0
        } else {
            // default to MODBUS
            accumulator = 0xFFFF
        }
        // main computation loop
        for byte in data {
            var tempByte = UInt16(byte)
            for _ in 0 ..< 8 {
                let temp1 = accumulator & 0x0001
                accumulator = accumulator >> 1
                let temp2 = tempByte & 0x0001
                tempByte = tempByte >> 1
                if (temp1 ^ temp2) == 1 {
                    accumulator = accumulator ^ polynomial
                }
            }
        }
        return accumulator
    }
}

Source here

hbk
  • 10,908
  • 11
  • 91
  • 124
  • if you have an `enum`, use `switch`, then you won't need comments like `default to MODBUS` when there is no actual default, just a second value. Why would you return `nil` for empty data? – Sulthan Jun 02 '18 at 13:00