Skip to content

Instantly share code, notes, and snippets.

@sp4c38
Last active January 8, 2022 18:14
Show Gist options
  • Select an option

  • Save sp4c38/4bf17bb6c5d479ee85a7611604d0c13d to your computer and use it in GitHub Desktop.

Select an option

Save sp4c38/4bf17bb6c5d479ee85a7611604d0c13d to your computer and use it in GitHub Desktop.

Revisions

  1. sp4c38 revised this gist Jan 8, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -26,7 +26,7 @@ exampleData >>>= 10 // ["00000000", "00001010", "00011111"]
    You can use following function to print the values of `Data` as binary for testing.
    ```swift
    func printData(_ data: Data) {
    var result: [String] = data.map { byte in
    let result: [String] = data.map { byte in
    var byteString = String(byte, radix: 2)
    (0..<(8-byteString.count)).forEach { _ in byteString = "0"+byteString }
    return byteString
  2. sp4c38 renamed this gist Jan 8, 2022. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  3. sp4c38 revised this gist Jan 8, 2022. 2 changed files with 1 addition and 1 deletion.
    File renamed without changes.
    2 changes: 1 addition & 1 deletion data_shift.md → description.md
    Original file line number Diff line number Diff line change
    @@ -19,7 +19,7 @@ exampleData >>>= 10 // ["00000000", "00001010", "00011111"]

    ### Note:
    - For left shift use either `<<` or `<<=`.
    - For right shift use either `>>>` or `>>>=`. Right shifts are always a logical right shift, arithmetic right shifts _are not_ supported.
    - For right shift use either `>>>` or `>>>=`. Right shifts are always logical right shifts, arithmetic right shifts _are not_ supported.
    - The above operators don't overwrite the built-in Swift bitwise operators like `<<` or `<<=`. They are only used if the left side of the expression is of type `Data`.

    ### Testing:
  4. sp4c38 created this gist Jan 8, 2022.
    93 changes: 93 additions & 0 deletions DataBitwiseShiftExtension.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,93 @@
    infix operator >>>=: AssignmentPrecedence
    infix operator >>>: BitwiseShiftPrecedence

    extension Data {
    private static let countBit: [Int: UInt8] = [
    1: 0b10000000, 2: 0b11000000, 3: 0b11100000, 4: 0b11110000,
    5: 0b11111000, 6: 0b11111100, 7: 0b11111110, 8: 0b11111111
    ]

    /// Performs a left shift.
    mutating private func leftShift(by shiftCount: Int) {
    guard self.count > 0, shiftCount > 0 else { return }
    guard self.count >= 2 else { self[0] <<= shiftCount; return }

    var truncateBytes: Int = shiftCount / 8
    truncateBytes = truncateBytes > self.count ? self.count : truncateBytes
    if truncateBytes >= 1 {
    self.indices.forEach { if $0 <= (truncateBytes-1) { self.remove(at: 0) } }
    }

    let truncateBits = shiftCount-(truncateBytes*8)
    if truncateBits >= 1 {
    for index in 0..<self.endIndex {
    self[index] <<= truncateBits
    guard index != self.endIndex-1 else { continue }
    var switchingBits = self[index+1] & Data.countBit[truncateBits]!
    switchingBits >>= 8-truncateBits
    self[index] |= switchingBits
    }
    }

    if truncateBytes >= 1 {
    (1...truncateBytes).forEach { _ in self.append(0) }
    }
    }

    private static let reverseCountBit: [Int: UInt8] = [
    1: 0b00000001, 2: 0b00000011, 3: 0b00000111, 4: 0b00001111,
    5: 0b00011111, 6: 0b00111111, 7: 0b01111111, 8: 0b11111111
    ]

    /// Performs a logical right shift.
    mutating private func rightShift(by shiftCount: Int) {
    guard self.count > 0, shiftCount > 0 else { return }
    guard self.count >= 2 else { self[0] >>= shiftCount; return }

    var truncateBytes: Int = shiftCount / 8 // Ignore decimal places
    truncateBytes = truncateBytes > self.count ? self.count : truncateBytes
    if truncateBytes >= 1 {
    let maxIndex = self.endIndex-1
    self.indices.reversed().forEach { if $0 > maxIndex-truncateBytes { self.remove(at: $0) } }
    }

    let truncateBits = shiftCount-(truncateBytes*8)
    if truncateBits >= 1 {
    for index in (0..<self.endIndex).reversed() {
    self[index] >>= truncateBits
    guard index != 0 else { continue }
    var switchingBits = self[index-1] & Data.reverseCountBit[truncateBits]!
    switchingBits <<= 8-truncateBits
    self[index] |= switchingBits
    }
    }

    if truncateBytes >= 1 {
    (1...truncateBytes).forEach { _ in self.insert(0, at: 0) }
    }
    }

    /// Performs a left shift.
    static func <<= (data: inout Data, shiftCount: Int) {
    data.leftShift(by: shiftCount)
    }

    /// Performs a left shift.
    static func << (data: Data, shiftCount: Int) -> Data {
    var copiedData = data
    copiedData.leftShift(by: shiftCount)
    return copiedData
    }

    /// Performs a logical right shift.
    static func >>>= (data: inout Data, shiftCount: Int) {
    data.rightShift(by: shiftCount)
    }

    /// Performs a logical right shift.
    static func >>> (data: Data, shiftCount: Int) -> Data {
    var copiedData = data
    copiedData.rightShift(by: shiftCount)
    return copiedData
    }
    }
    38 changes: 38 additions & 0 deletions data_shift.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    ## Bitwise shift multiple bytes in `Data`

    **Why?** Swift doesn't have a built-in way of shifting the bits from multiple bytes inside [`Data`](https://developer.apple.com/documentation/foundation/data).

    The below extension on `Data` allows you to do exactly that:
    ```swift
    var exampleData = Data([40, 127, 67]) // ["00101000", "01111111", "01000011"]
    // Left shift:
    exampleData << 10 // ["11111101", "00001100", "00000000"]
    // Left shift and set:
    exampleData <<= 10 // ["11111101", "00001100", "00000000"]

    exampleData = Data([40, 127, 67]) // ["00101000", "01111111", "01000011"]
    // Right shift:
    exampleData >>> 10 // ["00000000", "00001010", "00011111"]
    // Right shift and set
    exampleData >>>= 10 // ["00000000", "00001010", "00011111"]
    ```

    ### Note:
    - For left shift use either `<<` or `<<=`.
    - For right shift use either `>>>` or `>>>=`. Right shifts are always a logical right shift, arithmetic right shifts _are not_ supported.
    - The above operators don't overwrite the built-in Swift bitwise operators like `<<` or `<<=`. They are only used if the left side of the expression is of type `Data`.

    ### Testing:
    You can use following function to print the values of `Data` as binary for testing.
    ```swift
    func printData(_ data: Data) {
    var result: [String] = data.map { byte in
    var byteString = String(byte, radix: 2)
    (0..<(8-byteString.count)).forEach { _ in byteString = "0"+byteString }
    return byteString
    }
    print(result)
    }

    printData(exampleData)
    ```