{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}

-- |
-- Module: Data.Hash.Internal.Utils
-- Copyright: Copyright © 2021-2024 Lars Kuhtz <lakuhtz@gmail.com>
-- License: MIT
-- Maintainer: Lars Kuhtz <lakuhtz@gmail.com>
-- Stability: experimental
--
module Data.Hash.Internal.Utils
( B16ShortByteString(..)
, b16
) where

import Data.Bits
import Data.Char
import Data.String
import Data.ByteString qualified as B
import qualified Data.ByteString.Short as BS

import Text.Printf

-- -------------------------------------------------------------------------- --
-- Utils

newtype B16ShortByteString = B16ShortByteString
    { B16ShortByteString -> ShortByteString
_unB16ShortByteString :: BS.ShortByteString }

b16 :: B16ShortByteString -> B.ByteString
b16 :: B16ShortByteString -> ByteString
b16 (B16ShortByteString ShortByteString
b) = ShortByteString -> ByteString
BS.fromShort ShortByteString
b

instance Show B16ShortByteString where
    show :: B16ShortByteString -> String
show (B16ShortByteString ShortByteString
b) = (Word8 -> String) -> [Word8] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (String -> Word8 -> String
forall r. PrintfType r => String -> r
printf String
"%0.2x") ([Word8] -> String) -> [Word8] -> String
forall a b. (a -> b) -> a -> b
$ ShortByteString -> [Word8]
BS.unpack ShortByteString
b

-- This is rather inefficient. It is intended for string literals, debugging,
-- and testing.
--
instance IsString B16ShortByteString where
    fromString :: String -> B16ShortByteString
fromString String
l
        | Int -> Bool
forall a. Integral a => a -> Bool
odd (String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
l) =
            String -> B16ShortByteString
forall a. HasCallStack => String -> a
error String
"Data.Hash.Internal.Utils.B16ShortByteString.fromString: odd input length"
        | Bool
otherwise = ShortByteString -> B16ShortByteString
B16ShortByteString (ShortByteString -> B16ShortByteString)
-> ShortByteString -> B16ShortByteString
forall a b. (a -> b) -> a -> b
$ [Word8] -> ShortByteString
BS.pack ([Word8] -> ShortByteString) -> [Word8] -> ShortByteString
forall a b. (a -> b) -> a -> b
$ String -> [Word8]
forall {a}. Num a => String -> [a]
go String
l
      where
        go :: String -> [a]
go [] = [a]
forall a. Monoid a => a
mempty
        go [Char
_] = String -> [a]
forall a. HasCallStack => String -> a
error String
"Data.Hash.Internal.Utils.B16ShortByteString.fromString: odd input length"
        go (Char
a:Char
b:String
t) = Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Int -> Int
forall a. Bits a => a -> Int -> a
shiftL (Char -> Int
digitToInt Char
a) Int
4 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Char -> Int
digitToInt Char
b) a -> [a] -> [a]
forall a. a -> [a] -> [a]
: String -> [a]
go String
t