Skip to content

Instantly share code, notes, and snippets.

@krzyzanowskim
Last active October 9, 2019 20:17
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save krzyzanowskim/20669eb99fc4eca32572 to your computer and use it in GitHub Desktop.
Save krzyzanowskim/20669eb99fc4eca32572 to your computer and use it in GitHub Desktop.
Bit shifting with overflow protection using overflow operator "&"
//
// IntExtension.swift
// CryptoSwift
//
// Created by Marcin Krzyzanowski on 12/08/14.
// Copyright (C) 2014 Marcin Krzyżanowski <marcin.krzyzanowski@gmail.com>
// This software is provided 'as-is', without any express or implied warranty.
//
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
//
// - The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation is required.
// - Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
// - This notice may not be removed or altered from any source or binary distribution.
/*
Bit shifting with overflow protection using overflow operator "&".
Approach is consistent with standard overflow operators &+, &-, &*, &/
and introduce new overflow operators for shifting: &<<, &>>
Note: Works with unsigned integers values only
Usage
var i = 1 // init
var j = i &<< 2 //shift left
j &<<= 2 //shift left and assign
@see: https://medium.com/@krzyzanowskim/swiftly-shift-bits-and-protect-yourself-be33016ce071
*/
extension Int {
/** Shift bits to the left. All bits are shifted (including sign bit) */
private mutating func shiftLeft(count: Int) -> Int {
if (self == 0) {
return self;
}
let bitsCount = sizeof(Int) * 8
let shiftCount = Swift.min(count, bitsCount - 1)
var shiftedValue:Int = 0;
for bitIdx in 0..<bitsCount {
// if bit is set then copy to result and shift left 1
let bit = 1 << bitIdx
if ((self & bit) == bit) {
shiftedValue = shiftedValue | (bit << shiftCount)
}
}
self = shiftedValue
return self
}
/** Shift bits to the right. All bits are shifted (including sign bit) */
private mutating func shiftRight(count: UInt32) -> UInt32 {
if (self == 0) {
return self;
}
var bitsCount = UInt32(sizeofValue(self) * 8)
if (count >= bitsCount) {
return 0
}
var maxBitsForValue = UInt32(floor(log2(Double(self)) + 1))
var shiftCount = Swift.min(count, maxBitsForValue - 1)
var shiftedValue:UInt32 = 0;
for bitIdx in 0..<bitsCount {
// if bit is set then copy to result and shift left 1
var bit = 1 << bitIdx
if ((self & bit) == bit) {
shiftedValue = shiftedValue | (bit >> shiftCount)
}
}
self = shiftedValue
return self
}
}
// Left operator
infix operator &<<= {
associativity none
precedence 160
}
/** shift left and assign with bits truncation */
func &<<= (inout lhs: Int, rhs: Int) {
lhs.shiftLeft(rhs)
}
infix operator &<< {
associativity none
precedence 160
}
/** shift left with bits truncation */
func &<< (lhs: Int, rhs: Int) -> Int {
var l = lhs;
l.shiftLeft(rhs)
return l
}
// Right operator
infix operator &>>= {
associativity none
precedence 160
}
/** shift right and assign with bits truncation */
func &>>= (inout lhs: Int, rhs: Int) {
lhs.shiftRight(rhs)
}
infix operator &>> {
associativity none
precedence 160
}
/** shift right and assign with bits truncation */
func &>> (lhs: Int, rhs: Int) -> Int {
var l = lhs;
l.shiftRight(rhs)
return l
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment