Skip to content

Commit

Permalink
crc-8, crc-16 and crc-32 implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
nasirky committed Nov 13, 2020
1 parent c147c68 commit 58d9d34
Show file tree
Hide file tree
Showing 26 changed files with 749 additions and 1 deletion.
38 changes: 38 additions & 0 deletions Playgrounds/CRC16.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// CRC16.swift
//
// Copyright © 2020 QuickBird Studios. All rights reserved.
//

import Foundation

let crc16 = CRC16()

crc16.currentValue // initial value is 0
crc16.append(1.0)
crc16.append(1) // we are passing integer (If you want to pass UInt8, see the line below)
crc16.append(UInt8(1))
crc16.append([1, 20]) // We are passing [UInt8] (because that is the only type of array the append method accepts)

// get the current crc value
crc16.currentValue

// append more
crc16.append(5.6)
crc16.append(225)

// get the current crc value
crc16.currentValue

// reset the crc value
crc16.reset()

crc16.currentValue // value is 0 after reset


// CRC16's lookup table
crc16.lookupTable

// CRC16's lookup table (hexadecimal representation)
let lookupTableHexa = crc16.lookupTable.map { String($0, radix: 16) }
lookupTableHexa
45 changes: 45 additions & 0 deletions Playgrounds/CRC16.playground/Sources/CRC.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// CRC.swift
//
// Copyright © 2020 QuickBird Studios. All rights reserved.
//

import Foundation

public protocol CRC {
associatedtype Size

var currentValue: Size { get }

func append(_ bytes: [UInt8])

func reset()
}

extension CRC {
public func append(_ byte: UInt8) {
append([byte])
}

public func append<T: FixedWidthInteger>(_ integer: T) {
append(integer.bigEndianBytes)
}

public func append<T: BinaryFloatingPoint>(_ floatingPoint: T) {
append(floatingPoint.bigEndianBytes)
}
}

extension FixedWidthInteger {
public var bigEndianBytes: [UInt8] {
[UInt8](withUnsafeBytes(of: self.bigEndian) { Data($0) })
}
}

// Please keep in mind that Float has different representations and you need to make sure
// you are using the same representation as the system you are communicating with
extension BinaryFloatingPoint {
public var bigEndianBytes: [UInt8] {
[UInt8](withUnsafeBytes(of: self) { Data($0) }).reversed()
}
}
60 changes: 60 additions & 0 deletions Playgrounds/CRC16.playground/Sources/CRC16.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
//
// CRC16.swift
//
// Copyright © 2020 QuickBird Studios. All rights reserved.
//

import Foundation

/// Class to conveniently calculate CRC-16. It uses the CRC16-CCITT polynomial (0x1021) by default
public class CRC16: CRC {

/// The table that stores the precomputed remainders for all possible divisions for 1 byte
/// This table is used as a lookup (to speed up the calculation)
public let lookupTable: [UInt16]

private(set) public var currentValue: UInt16 = 0

/// Creates the instance for calculating CRC
/// - Parameter polynomial: The polynomial to use. It uses CRC16-CCITT's polynomial (0x1021) by default
public init(polynomial: UInt16 = 0x1021) {
/// Generates the lookup table (make sure to generate it only once)
self.lookupTable = (0...255).map { Self.crc16(for: UInt8($0), polynomial: polynomial) }
}

public func append(_ bytes: [UInt8]) {
currentValue = crc16(for: bytes, initialValue: currentValue)
}

public func reset() {
currentValue = 0
}
}

/// Same code as the blog article
extension CRC16 {
/// Caculates CRC-16 of an array of Bytes (UInt8)
private func crc16(for inputs: [UInt8], initialValue: UInt16 = 0) -> UInt16 {
inputs.reduce(initialValue) { remainder, byte in
let bigEndianInput = UInt16(byte).bigEndian
let index = (bigEndianInput ^ remainder) >> 8
return lookupTable[Int(index)] ^ (remainder << 8)
}
}

/// Calculates the CRC-16 of 1 Byte
private static func crc16(for input: UInt8, polynomial: UInt16) -> UInt16 {
var result = UInt16(input).bigEndian
for _ in 0..<8 {
let isMostSignificantBitOne = result & 0x8000 != 0

result = result << 1

if isMostSignificantBitOne {
result = result ^ polynomial
}
}

return result
}
}
4 changes: 4 additions & 0 deletions Playgrounds/CRC16.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='ios' buildActiveScheme='true'>
<timeline fileName='timeline.xctimeline'/>
</playground>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>CRC16 (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>
37 changes: 37 additions & 0 deletions Playgrounds/CRC32.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// CRC32.playground
//
// Copyright © 2020 QuickBird Studios. All rights reserved.
//

import Foundation

let crc32 = CRC32()

crc32.currentValue // initial value is 0
crc32.append(1.0)
crc32.append(1) // we are passing integer (If you want to pass UInt8, see the line below)
crc32.append(UInt8(1))
crc32.append([1, 20]) // We are passing [UInt8] (because that is the only type of array the append method accepts)

// get the current crc value
crc32.currentValue

// append more
crc32.append(5.6)
crc32.append(225)

// get the current crc value
crc32.currentValue

// reset the crc value
crc32.reset()

crc32.currentValue // value is 0 after reset


// CRC32's lookup table
crc32.lookupTable

// CRC32's lookup table (hexadecimal representation)
let lookupTableHexa = crc32.lookupTable.map { String($0, radix: 16) }
47 changes: 47 additions & 0 deletions Playgrounds/CRC32.playground/Sources/CRC.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// CRC.swift
//
// Copyright © 2020 QuickBird Studios. All rights reserved.
//

import Foundation

public protocol CRC {
associatedtype Size

var currentValue: Size { get }

func append(_ bytes: [UInt8])

func reset()
}

extension CRC {
public func append(_ byte: UInt8) {
append([byte])
}

public func append<T: FixedWidthInteger>(_ integer: T) {
append(integer.bigEndianBytes)
}

public func append<T: BinaryFloatingPoint>(_ floatingPoint: T) {
append(floatingPoint.bigEndianBytes)
}
}

// MARK: - Convenience Extension Methods

extension FixedWidthInteger {
public var bigEndianBytes: [UInt8] {
[UInt8](withUnsafeBytes(of: self.bigEndian) { Data($0) })
}
}

// `BinaryFloatingPoint` conforms to 754-2008 - IEEE Standard for Floating-Point Arithmetic (https://ieeexplore.ieee.org/document/4610935)
// If you target system is using a different floating point representation, you need to adapt accordingly
extension BinaryFloatingPoint {
public var bigEndianBytes: [UInt8] {
[UInt8](withUnsafeBytes(of: self) { Data($0) }).reversed()
}
}
59 changes: 59 additions & 0 deletions Playgrounds/CRC32.playground/Sources/CRC32.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// CRC32.swift
//
// Copyright © 2020 QuickBird Studios. All rights reserved.
//

import Foundation

/// Class to conveniently calculate CRC-32 It uses the CRC32 polynomial (0x04C11DB7) by default
public class CRC32: CRC {

/// The table that stores the precomputed remainders for all possible divisions for 1 byte
/// This table is used as a lookup (to speed up the calculation)
public let lookupTable: [UInt32]

private(set) public var currentValue: UInt32 = 0

/// Creates the instance for calculating CRC
/// - Parameter polynomial: The polynomial to use. It uses CRC32's polynomial (0x04C11DB7) by default
public init(polynomial: UInt32 = 0x04C11DB7) {
/// Generates the lookup table (make sure to generate it only once)
self.lookupTable = (0...255).map { Self.crc32(for: UInt8($0), polynomial: polynomial) }
}

public func append(_ bytes: [UInt8]) {
currentValue = crc32(for: bytes, initialValue: currentValue)
}

public func reset() {
currentValue = 0
}
}

extension CRC32 {
/// Caculates CRC-32 of an array of Bytes (UInt8)
private func crc32(for inputs: [UInt8], initialValue: UInt32 = 0) -> UInt32 {
inputs.reduce(initialValue) { remainder, byte in
let bigEndianInput = UInt32(byte).bigEndian
let index = (bigEndianInput ^ remainder) >> 24
return lookupTable[Int(index)] ^ (remainder << 8)
}
}

/// Calculates the CRC-32 of 1 Byte
private static func crc32(for input: UInt8, polynomial: UInt32) -> UInt32 {
var result = UInt32(input).bigEndian
for _ in 0..<8 {
let isMostSignificantBitOne = result & 0x80000000 != 0

result = result << 1

if isMostSignificantBitOne {
result = result ^ polynomial
}
}

return result
}
}
4 changes: 4 additions & 0 deletions Playgrounds/CRC32.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='ios' buildActiveScheme='true'>
<timeline fileName='timeline.xctimeline'/>
</playground>

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
38 changes: 38 additions & 0 deletions Playgrounds/CRC8.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// CRC8.playground
//
// Copyright © 2020 QuickBird Studios. All rights reserved.
//

import Foundation

let crc8 = CRC8()

crc8.currentValue // initial value is 0
crc8.append(1.0)
crc8.append(1) // we are passing integer (If you want to pass UInt8, see the line below)
crc8.append(UInt8(1))
crc8.append([1, 20]) // We are passing [UInt8] (because that is the only type of array the append method accepts)

// get the current crc value
crc8.currentValue

// append more
crc8.append(5.6)
crc8.append(225)

// get the current crc value
crc8.currentValue

// reset the crc value
crc8.reset()

crc8.currentValue // value is 0 after reset


// CRC8's lookup table
crc8.lookupTable

// CRC8's lookup table (hexadecimal representation)
let lookupTableHexa = crc8.lookupTable.map { String($0, radix: 16) }
lookupTableHexa
Loading

0 comments on commit 58d9d34

Please sign in to comment.