zigzag-0.0.1.0: src/Data/Word/Zigzag.hs
{-# LANGUAGE TypeApplications #-}
-- | Zigzag encoding maps signed integers to unsigned integers so that numbers
-- with a small absolute value (for instance, -1) have a small varint encoded
-- value too. It does this in a way that "zig-zags" back and forth through the
-- positive and negative integers, so that -1 is encoded as 1, 1 is encoded as
-- 2, -2 is encoded as 3, and so on.
--
-- > zigzag(n) = { 2 * n if 0 <= n
-- > { -2 * n - 1 if n < 0
--
-- This description was adapted from
-- https://developers.google.com/protocol-buffers/docs/encoding#signed-ints
-- which is released under https://creativecommons.org/licenses/by/4.0/
module Data.Word.Zigzag
( toZigzag
, fromZigzag
, toZigzagNative
, fromZigzagNative
, toZigzag32
, fromZigzag32
, toZigzag64
, fromZigzag64
) where
import Data.Bits (unsafeShiftL, unsafeShiftR, complement, (.&.), xor)
import Data.Int(Int32,Int64)
import Data.Word(Word32,Word64)
import Numeric.Natural (Natural)
-- | Encode a big integer with zigzag.
--
-- If you know the size of the data, it is likely more efficient to use one of
-- 'toZigzagNative', 'toZigzag32', or 'toZigzag64'.
toZigzag :: Integer -> Natural
toZigzag n
| 0 <= n = fromIntegral $ 2 * n
| otherwise = fromIntegral $ (-2) * n - 1
-- | Decode a zigzag-encoded big ingeter.
--
-- If you know the size of the data, it is likely more efficient to use one of
-- 'fromZigzagNative', 'fromZigzag32', or 'fromZigzag64'.
fromZigzag :: Natural -> Integer
fromZigzag n
| n `mod` 2 == 0 = fromIntegral $ n `div` 2
| otherwise = negate . fromIntegral $ (n + 1) `div` 2
-- | Encode a native-size integer with zigzag.
--
-- In C, this is:
--
-- > (n << 1) ^ (n >> (CHAR_BIT * sizeof(int) - 1))
toZigzagNative :: Int -> Word
toZigzagNative = fromIntegral . toZigzag64 . fromIntegral
-- | Decode a native-size zigzag-encoded integer.
--
-- In C, this is:
--
-- > (n >> 1) ^ (~(n & 1) + 1)
fromZigzagNative :: Word -> Int
fromZigzagNative = fromIntegral . fromZigzag64 . fromIntegral
-- | Encode a 32-bit integer with zigzag.
--
-- In C, this is:
--
-- > (n << 1) ^ (n >> 31)
toZigzag32 :: Int32 -> Word32
toZigzag32 = fromIntegral . toZigzag64 . fromIntegral
-- | Decode a 32-bit zigzag-encoded integer.
--
-- In C, this is:
--
-- > (n >> 1) ^ (~(n & 1) + 1)
fromZigzag32 :: Word32 -> Int32
fromZigzag32 = fromIntegral . fromZigzag64 . fromIntegral
-- | Encode a 64-bit integer with zigzag.
--
-- In C, this is:
--
-- > (n << 1) ^ (n >> 63)
toZigzag64 :: Int64 -> Word64
toZigzag64 i = fromIntegral @Int64 @Word64 $
(i `unsafeShiftL` 1) `xor` (i `unsafeShiftR` 63)
-- | Decode a 64-bit zigzag-encoded integer.
--
-- In C, this is:
--
-- > (n >> 1) ^ (~(n & 1) + 1)
fromZigzag64 :: Word64 -> Int64
fromZigzag64 n = fromIntegral $
(n `unsafeShiftR` 1) `xor` (complement (n .&. 1) + 1)