-- Copyright (C) 2017-2020 DBotThePony -- Permission is hereby granted, free of charge, to any person obtaining a copy -- of this software and associated documentation files (the "Software"), to deal -- in the Software without restriction, including without limitation the rights -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -- of the Software, and to permit persons to whom the Software is furnished to do so, -- subject to the following conditions: -- The above copyright notice and this permission notice shall be included in all copies -- or substantial portions of the Software. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -- INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -- PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -- FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -- DEALINGS IN THE SOFTWARE. DLib.bitworker = DLib.bitworker or {} local table = table local DLib = DLib local math = math local bitworker = DLib.bitworker local type = type local ipairs = ipairs local bit = bit local lshift = bit.lshift local rshift = bit.rshift local band = bit.band local bor = bit.bor local bxor = bit.bxor local bnot = bit.bnot local pow = math.pow local function isValidNumber(numIn) return type(numIn) == 'number' and numIn == numIn and numIn ~= math.huge and numIn ~= -math.huge end local function table_insert(tabIn, val) tabIn[#tabIn + 1] = val end --[[ @doc @fname DLib.bitworker.IntegerToBinary @args number value, number bitsAmount @desc function is a bit slow (for intensive use) this uses forwarding bit for negative values (the one used in networking (not in Sorse Engine networking wtf!)/etc/etc) @enddesc @returns table: bits ]] function bitworker.IntegerToBinary(numberIn, bitsNum) if not isValidNumber(numberIn) then local vr = {} for i = 1, bitsNum do vr[i] = 0 end return vr end local bits = {} local sign = numberIn >= 0 and 0 or 1 if sign == 1 then numberIn = -numberIn end bits[1] = sign for i = 2, bitsNum do bits[i] = 0 end for i = 2, bitsNum do if numberIn == 0 then break end local div = numberIn % 2 numberIn = (numberIn - div) / 2 bits[bitsNum - i + 2] = div end return bits end --[[ @doc @fname DLib.bitworker.IntegerToBinary2 @args number value, number bitsAmount @desc function is a bit slow (for intensive use) this uses overflow for negative values (the one used by Sorse Engine) @enddesc @returns table: bits ]] function bitworker.IntegerToBinary2(numberIn, bitsNum) local max = math.pow(2, bitsNum) if numberIn < 0 then numberIn = max + numberIn elseif numberIn > max then numberIn = numberIn - max end local bits = {} for i = 1, bitsNum do bits[i] = 0 end for i = 1, bitsNum do if numberIn == 0 then break end local div = numberIn % 2 numberIn = (numberIn - div) / 2 bits[bitsNum - i + 1] = div end return bits end --[[ @doc @fname DLib.bitworker.BinaryToUInteger @args table bits @desc reversal of UIntegerToBinary @enddesc @returns number ]] function bitworker.BinaryToUInteger(inputTable) local amount = #inputTable local output = 0 for i = 1, amount do if inputTable[i] == 1 then output = output + math.pow(2, amount - i) end end return output end --[[ @doc @fname DLib.bitworker.BinaryToInteger @args table bits @desc reversal of IntegerToBinary @enddesc @returns number ]] function bitworker.BinaryToInteger(inputTable) local direction = inputTable[1] local amount = #inputTable local output = 0 for i = 2, amount do if inputTable[i] == 1 then output = output + math.pow(2, amount - i) end end if direction == 0 then return output else return -output end end --[[ @doc @fname DLib.bitworker.BinaryToInteger2 @args table bits @desc reversal of IntegerToBinary2 @enddesc @returns number ]] function bitworker.BinaryToInteger2(bits) local bitsNum = #bits local max = math.pow(2, bitsNum - 1) - 1 local output = 0 for i = 1, bitsNum do if bits[i] == 1 then output = output + math.pow(2, bitsNum - i) end end if output > max then output = output - math.pow(2, bitsNum) end return output end --[[ @doc @fname DLib.bitworker.UIntegerToBinary @args number value, number bitsAmount @desc negative values are handled by overflow so please don't pass negative values ok? @enddesc @returns table: bits ]] function bitworker.UIntegerToBinary(numberIn, bitsNum) if not isValidNumber(numberIn) then local vr = {} for i = 1, bitsNum do vr[i] = 0 end return vr end if numberIn < 0 then numberIn = math.pow(2, bitsNum) + numberIn end local bits = {} for i = 1, bitsNum do bits[i] = 0 end for i = 1, bitsNum do if numberIn == 0 then break end local div = numberIn % 2 numberIn = (numberIn - div) / 2 bits[bitsNum - i + 1] = div end return bits end --[[ @doc @fname DLib.bitworker.NumberToMantiss @args number value, number bitsAllowed @returns table: bits number: exponent ]] function bitworker.NumberToMantiss(numberIn, bitsAllowed) if not isValidNumber(numberIn) then local bits = {} for i = 1, bitsAllowed do bits[i] = 0 end return bits end local exp = 0 numberIn = math.abs(numberIn) local lastMult = numberIn % 1 if numberIn >= 2 then -- try to normalize number to be less than 2 -- shift to right while numberIn >= 2 do numberIn = numberIn / 2 exp = exp + 1 end end -- if our number is less than one, shift to left if exp == 0 and numberIn < 1 then while numberIn < 1 do numberIn = numberIn * 2 exp = exp - 1 end end -- if number is not a zero, it is known amoung all computers that -- first bit of mantissa is always 1 -- so let's assume so numberIn = numberIn - 1 local bits = {} for i = 1, bitsAllowed do numberIn = numberIn * 2 if numberIn >= 1 then table_insert(bits, 1) numberIn = numberIn - 1 else table_insert(bits, 0) end end return bits, exp end --[[ @doc @fname DLib.bitworker.NumberToMantissFast @args number value, number bitsAllowed @returns number: fist part of mantiss (32-bit integer) number: second part of mantiss (32-bit integer) number: exponent ]] function bitworker.NumberToMantissFast(numberIn, bitsAllowed) if not isValidNumber(numberIn) then return 0 end local exp, numberOut1, numberOut2 = 0, 0, 0 numberIn = math.abs(numberIn) local lastMult = numberIn % 1 if numberIn >= 2 then -- try to normalize number to be less than 2 -- shift to right while numberIn >= 2 do numberIn = numberIn / 2 exp = exp + 1 end end -- if our number is less than one, shift to left if exp == 0 and numberIn < 1 then while numberIn < 1 do numberIn = numberIn * 2 exp = exp - 1 end end -- if number is not a zero, it is known amoung all computers that -- first bit of mantissa is always 1 -- so let's assume so numberIn = numberIn - 1 local dest1, dest2 = math.min(32, bitsAllowed), math.min(32, bitsAllowed - 32) for i = 1, dest1 do numberIn = numberIn * 2 if numberIn >= 1 then numberOut1 = bor(numberOut1, lshift(1, dest1 - i)) numberIn = numberIn - 1 end end for i = 1, dest2 do numberIn = numberIn * 2 if numberIn >= 1 then numberOut2 = bor(numberOut2, lshift(1, dest2 - i)) numberIn = numberIn - 1 end end return numberOut1, numberOut2, exp end --[[ @doc @fname DLib.bitworker.MantissToNumber @args table bits, number exponent @returns number ]] function bitworker.MantissToNumber(bitsIn, exp) exp = exp or 0 local num = 0 for i = 1, #bitsIn do if bitsIn[i] == 1 then num = num + pow(2, -i) end end return pow(2, exp) * (1 + num) end --[[ @doc @fname DLib.bitworker.MantissToNumberFast @args number mantiss1, number mantiss2, number exponent, number bitsIn @returns number ]] function bitworker.MantissToNumberFast(numberIn1, numberIn2, exp, bitsIn) exp = exp or 0 numberIn1 = numberIn1 or 0 numberIn2 = numberIn2 or 0 local num = 0 local dest1 = math.min(32, bitsIn) - 1 local dest2 = math.min(32, bitsIn - 32) - 1 numberIn1 = lshift(numberIn1, math.max(32 - bitsIn, 0)) numberIn2 = lshift(numberIn2, 63 - bitsIn) for i = 0, dest1 do if band(rshift(numberIn1, 31 - i), 1) == 1 then num = num + pow(2, -(i + 1)) end end for i = 0, dest2 do if band(rshift(numberIn2, 31 - i), 1) == 1 then num = num + pow(2, -(i + 32)) end end return pow(2, exp) * (1 + num) end --[[ @doc @fname DLib.bitworker.FloatToBinaryIEEE @args number value, number bitsForExponent, number bitsForMantissa @returns table: bits ]] -- final range is bitsExponent + bitsMantissa + 2 -- where 2 is two bits which one forwards number sign and one forward exponent sign function bitworker.FloatToBinaryIEEE(numberIn, bitsExponent, bitsMantissa) if not isValidNumber(numberIn) or numberIn == 0 then local bits = {} for i = 0, bitsExponent + bitsMantissa do table_insert(bits, 0) end return bits end local bits = {numberIn >= 0 and 0 or 1} local mantissa, exp = bitworker.NumberToMantiss(numberIn, bitsMantissa) local expBits = bitworker.UIntegerToBinary(exp + math.pow(2, bitsExponent - 1) - 1, bitsExponent) table.append(bits, expBits) table.append(bits, mantissa) return bits end --[[ @doc @fname DLib.bitworker.FastFloatToBinaryIEEE @args number value @returns number: integer representation of float ]] function bitworker.FastFloatToBinaryIEEE(numberIn) if not isValidNumber(numberIn) or numberIn == 0 then return 0 end local mantissa1, mantissa2, exp = bitworker.NumberToMantissFast(numberIn, 23) return bor(lshift(numberIn >= 0 and 0 or 1, 31), lshift(exp + 127, 23), mantissa1) end --[[ @doc @fname DLib.bitworker.FastDoubleToBinaryIEEE @args number value @returns number: integer representation of double (first part) number: integer representation of double (second part) ]] function bitworker.FastDoubleToBinaryIEEE(numberIn) if not isValidNumber(numberIn) or numberIn == 0 then return 0, 0 end local mantissa1, mantissa2, exp = bitworker.NumberToMantissFast(numberIn, 52) return bor(lshift(numberIn >= 0 and 0 or 1, 31), lshift(exp + 1023, 20), rshift(mantissa1, 12)), bor(lshift(band(mantissa1, 4095), 20), mantissa2) end --[[ @doc @fname DLib.bitworker.BinaryToFloatIEEE @args table bits, number bitsExponent, number bitsMantissa @returns number ]] function bitworker.BinaryToFloatIEEE(bitsIn, bitsExponent, bitsMantissa) local valid = false for i = 1, #bitsIn do if bitsIn[i] ~= 0 then valid = true break end end if not valid then return 0 end local forward = bitsIn[1] local exponent = table.gcopyRange(bitsIn, 2, 2 + bitsExponent - 1) local exp = bitworker.BinaryToUInteger(exponent) local mantissa = table.gcopyRange(bitsIn, 2 + bitsExponent) local value = bitworker.MantissToNumber(mantissa, exp - math.pow(2, bitsExponent - 1) + 1) if forward == 0 then return value end return -value end --[[ @doc @fname DLib.bitworker.FastBinaryToFloatIEEE @args number integerRepresentation @returns number: float ]] function bitworker.FastBinaryToFloatIEEE(numberIn) if numberIn == 0 then return 0 end local point = rshift(numberIn, 31) local exp = band(rshift(numberIn, 23), 0xFF) - 127 local mantissa = band(numberIn, 0x7FFFFF) local value = bitworker.MantissToNumberFast(mantissa, 0, exp, 23) if point == 0 then return value end return -value end --[[ @doc @fname DLib.bitworker.FastBinaryToDoubleIEEE @args number integerRepresentation1, number integerRepresentation2 @returns number: double ]] function bitworker.FastBinaryToDoubleIEEE(numberIn1, numberIn2) if numberIn == 0 then return 0 end local point = rshift(numberIn1, 31) local exp = band(rshift(numberIn1, 20), 0x7FF) - 1023 local mantissa1 = lshift(band(numberIn1, 0xFFFFF), 12) local mantissa2 = band(numberIn2, 0xFFFFF) mantissa1 = bor(mantissa1, rshift(numberIn2, 20)) local value = bitworker.MantissToNumberFast(mantissa1, mantissa2, exp, 52) if point == 0 then return value end return -value end --[[ @doc @fname DLib.bitworker.BitsToBytes @args table bits @desc a table consist of `{1, 1, 1, 0, 0, 0, 1, 0}` turns into `{226}` for example `{1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1}` turns into `{226, 225}` @descdesc @returns table: bytes ]] function bitworker.BitsToBytes(bitsIn) assert(#bitsIn % 8 == 0, 'Not full bytes') local output = {} for i = 1, #bitsIn / 8 do output[i] = bitsIn[(i - 1) * 8 + 8] + bitsIn[(i - 1) * 8 + 7] * 2 + bitsIn[(i - 1) * 8 + 6] * 4 + bitsIn[(i - 1) * 8 + 5] * 8 + bitsIn[(i - 1) * 8 + 4] * 16 + bitsIn[(i - 1) * 8 + 3] * 32 + bitsIn[(i - 1) * 8 + 2] * 64 + bitsIn[(i - 1) * 8 + 1] * 128 end return output end