{-# LANGUAGE CPP                 #-}
{-# LANGUAGE PatternSynonyms     #-}


-- | Internal module. Not part of the public API.
module Data.PackStream.Tags
    ( -- * Masks
      pattern MASK_HIGH_NIBBLE
    , pattern MASK_LOW_NIBBLE
      -- * Null and Boolean
    , pattern TAG_Null
    , pattern TAG_false
    , pattern TAG_true
      -- * Integers
    , is_TAG_TINY_INT
    , pattern TAG_INT_8
    , pattern TAG_INT_16
    , pattern TAG_INT_32
    , pattern TAG_INT_64
      -- * Float
    , pattern TAG_Float
      -- * Byte arrays
    , pattern TAG_BYTE_ARRAY_8
    , pattern TAG_BYTE_ARRAY_16
    , pattern TAG_BYTE_ARRAY_32
      -- * Strings
    , pattern TAG_STRING_SHORT
    , is_TAG_STRING_SHORT
    , pattern TAG_STRING_8
    , pattern TAG_STRING_16
    , pattern TAG_STRING_32
      -- * Lists
    , pattern TAG_LIST_SHORT
    , is_TAG_LIST_SHORT
    , pattern TAG_LIST_8
    , pattern TAG_LIST_16
    , pattern TAG_LIST_32
      -- * Dictionaries
    , pattern TAG_DICTIONARY_SHORT
    , is_TAG_DICTIONARY_SHORT
    , pattern TAG_DICTIONARY_8
    , pattern TAG_DICTIONARY_16
    , pattern TAG_DICTIONARY_32
      -- * Structures
    , is_TAG_STRUCTURE
    ) where

import           Compat.Prelude

{-

-- | Test whether tag is a fixint
is_TAG_fixint :: Word8 -> Bool
is_TAG_fixint tag = (tag .&. TAG_MASK_fixintp == TAG_fixintp)
                 || (tag .&. TAG_MASK_fixintn == TAG_fixintn)
{-# INLINE is_TAG_fixint #-}

pattern TAG_fixintn       = 0xe0 -- 0b111xxxxx [0xe0 .. 0xff] / [-32 .. -1]
pattern TAG_MASK_fixintn  = 0xe0 -- 0b11100000

pattern TAG_fixintp       = 0x00 -- 0b0xxxxxxx [0x00 .. 0x7f] / [0 .. 127]
pattern TAG_MASK_fixintp  = 0x80 -- 0b10000000

-- | Test whether tag is a fixmap and return embedded-size if it is
is_TAG_fixmap :: Word8 -> Maybe Word32
is_TAG_fixmap t
  | t .&. TAG_MASK_fixmap == TAG_fixmap = Just $! intCast (t .&. complement TAG_MASK_fixmap)
  | otherwise                               = Nothing
{-# INLINE is_TAG_fixmap #-}

pattern TAG_fixmap        = 0x80 -- 0b1000xxxx [0x80 .. 0x8f]
pattern TAG_MASK_fixmap   = 0xf0 -- 0b11110000

-- | Test whether tag is a fixarray and return embedded-size if it is
is_TAG_fixarray :: Word8 -> Maybe Word32
is_TAG_fixarray t
  | t .&. TAG_MASK_fixarray == TAG_fixarray = Just $! intCast (t .&. complement TAG_MASK_fixarray)
  | otherwise                               = Nothing
{-# INLINE is_TAG_fixarray #-}

pattern TAG_fixarray      = 0x90 -- 0b1001xxxx [0x90 .. 0x9f]
pattern TAG_MASK_fixarray = 0xf0 -- 0b11110000

-- | Test whether tag is a fixstr and return embedded-size if it is
is_TAG_fixstr :: Word8 -> Maybe Word32
is_TAG_fixstr t
  | t .&. TAG_MASK_fixstr == TAG_fixstr = Just $! intCast (t .&. complement TAG_MASK_fixstr)
  | otherwise                           = Nothing
{-# INLINE is_TAG_fixstr #-}

pattern TAG_fixstr        = 0xa0 -- 0b101xxxxx [0xa0 .. 0xbf]
pattern TAG_MASK_fixstr   = 0xe0 -- 0b11100000

pattern TAG_nil           = 0xc0 -- 0b11000000
pattern TAG_reserved_C1   = 0xc1 -- 0b11000001
pattern TAG_false         = 0xc2 -- 0b11000010
pattern TAG_true          = 0xc3 -- 0b11000011

pattern TAG_bin8          = 0xc4 -- 0b11000100
pattern TAG_bin16         = 0xc5 -- 0b11000101
pattern TAG_bin32         = 0xc6 -- 0b11000110

pattern TAG_ext8          = 0xc7 -- 0b11000111
pattern TAG_ext16         = 0xc8 -- 0b11001000
pattern TAG_ext32         = 0xc9 -- 0b11001001

pattern TAG_float32       = 0xca -- 0b11001010
pattern TAG_float64       = 0xcb -- 0b11001011

pattern TAG_uint8         = 0xcc -- 0b11001100
pattern TAG_uint16        = 0xcd -- 0b11001101
pattern TAG_uint32        = 0xce -- 0b11001110
pattern TAG_uint64        = 0xcf -- 0b11001111

pattern TAG_int8          = 0xd0 -- 0b11010000
pattern TAG_int16         = 0xd1 -- 0b11010001
pattern TAG_int32         = 0xd2 -- 0b11010010
pattern TAG_int64         = 0xd3 -- 0b11010011

pattern TAG_fixext1       = 0xd4 -- 0b11010100
pattern TAG_fixext2       = 0xd5 -- 0b11010101
pattern TAG_fixext4       = 0xd6 -- 0b11010110
pattern TAG_fixext8       = 0xd7 -- 0b11010111
pattern TAG_fixext16      = 0xd8 -- 0b11011000

pattern TAG_str8          = 0xd9 -- 0b11011001
pattern TAG_str16         = 0xda -- 0b11011010
pattern TAG_str32         = 0xdb -- 0b11011011

pattern TAG_array16       = 0xdc -- 0b11011100
pattern TAG_array32       = 0xdd -- 0b11011101

pattern TAG_map16         = 0xde -- 0b11011110
pattern TAG_map32         = 0xdf -- 0b11011111

-- NOTE: Currently the PackStream specification only defines the @-1@
-- extension type (for timestamps). All remaining negative Int8
-- type-ids are reserved for future use by the PackStream.

-- Used by "Data.PackStream.Timestamp"
pattern XTAG_Timestamp = -1 :: Int8

-}

-- | Bitmask selecting the high nibble (upper 4 bits) of a byte.
pattern MASK_HIGH_NIBBLE :: (Eq a, Num a) => a
pattern $mMASK_HIGH_NIBBLE :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bMASK_HIGH_NIBBLE :: forall a. (Eq a, Num a) => a
MASK_HIGH_NIBBLE = 0xF0

-- | Bitmask selecting the low nibble (lower 4 bits) of a byte.
pattern MASK_LOW_NIBBLE :: (Eq a, Num a) => a
pattern $mMASK_LOW_NIBBLE :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bMASK_LOW_NIBBLE :: forall a. (Eq a, Num a) => a
MASK_LOW_NIBBLE  = 0x0F


-- https://neo4j.com/docs/bolt/current/packstream/

-- | Tag byte for Null (0xC0).
pattern TAG_Null :: (Eq a, Num a) => a
pattern $mTAG_Null :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_Null :: forall a. (Eq a, Num a) => a
TAG_Null = 0xC0

-- | Tag byte for boolean false (0xC2).
pattern TAG_false :: (Eq a, Num a) => a
pattern $mTAG_false :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_false :: forall a. (Eq a, Num a) => a
TAG_false = 0xC2

-- | Tag byte for boolean true (0xC3).
pattern TAG_true :: (Eq a, Num a) => a
pattern $mTAG_true :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_true :: forall a. (Eq a, Num a) => a
TAG_true = 0xC3

-- | Test whether tag is a TINY_INT
is_TAG_TINY_INT :: Word8 -> Bool
is_TAG_TINY_INT :: Word8 -> Bool
is_TAG_TINY_INT Word8
tag = Word8
tag Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
<= Word8
0x7F Bool -> Bool -> Bool
|| Word8
tag Word8 -> Word8 -> Bool
forall a. Ord a => a -> a -> Bool
>= Word8
0xF0
{-# INLINE is_TAG_TINY_INT #-}

-- | Tag byte for 8-bit signed integer (0xC8).
pattern TAG_INT_8 :: (Eq a, Num a) => a
pattern $mTAG_INT_8 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_INT_8 :: forall a. (Eq a, Num a) => a
TAG_INT_8  = 0xC8

-- | Tag byte for 16-bit signed integer (0xC9).
pattern TAG_INT_16 :: (Eq a, Num a) => a
pattern $mTAG_INT_16 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_INT_16 :: forall a. (Eq a, Num a) => a
TAG_INT_16 = 0xC9

-- | Tag byte for 32-bit signed integer (0xCA).
pattern TAG_INT_32 :: (Eq a, Num a) => a
pattern $mTAG_INT_32 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_INT_32 :: forall a. (Eq a, Num a) => a
TAG_INT_32 = 0xCA

-- | Tag byte for 64-bit signed integer (0xCB).
pattern TAG_INT_64 :: (Eq a, Num a) => a
pattern $mTAG_INT_64 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_INT_64 :: forall a. (Eq a, Num a) => a
TAG_INT_64 = 0xCB

-- Not supporting this (except for TINY_INT):
-- Some marker bytes can be used to carry the value of a small integer as well as its type. These markers can be identified by a zero high-order bit (for positive values) or by a high-order nibble containing only ones (for negative values). Specifically, values between 00 and 7F inclusive can be directly translated to and from positive integers with the same value. Similarly, values between F0 and FF inclusive can do the same for negative numbers between -16 and -1.

-- | Tag byte for 64-bit float (0xC1).
pattern TAG_Float :: (Eq a, Num a) => a
pattern $mTAG_Float :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_Float :: forall a. (Eq a, Num a) => a
TAG_Float = 0xC1

-- | Tag byte for byte array with 8-bit length (0xCC).
pattern TAG_BYTE_ARRAY_8 :: (Eq a, Num a) => a
pattern $mTAG_BYTE_ARRAY_8 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_BYTE_ARRAY_8 :: forall a. (Eq a, Num a) => a
TAG_BYTE_ARRAY_8 = 0xCC

-- | Tag byte for byte array with 16-bit length (0xCD).
pattern TAG_BYTE_ARRAY_16 :: (Eq a, Num a) => a
pattern $mTAG_BYTE_ARRAY_16 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_BYTE_ARRAY_16 :: forall a. (Eq a, Num a) => a
TAG_BYTE_ARRAY_16 = 0xCD

-- | Tag byte for byte array with 32-bit length (0xCE).
pattern TAG_BYTE_ARRAY_32 :: (Eq a, Num a) => a
pattern $mTAG_BYTE_ARRAY_32 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_BYTE_ARRAY_32 :: forall a. (Eq a, Num a) => a
TAG_BYTE_ARRAY_32 = 0xCE

-- | Base tag byte for short strings with length embedded in the low nibble (0x80).
pattern TAG_STRING_SHORT :: (Eq a, Num a) => a
pattern $mTAG_STRING_SHORT :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_STRING_SHORT :: forall a. (Eq a, Num a) => a
TAG_STRING_SHORT = 0x80

-- is_TAG_STRING_SHORT :: Word8 -> Bool
-- is_TAG_STRING_SHORT tag = tag >= 0x80 && tag <= 0x8F
-- {-# INLINE is_TAG_STRING_SHORT #-}

-- | Test whether tag is a short string; returns embedded length if so.
is_TAG_STRING_SHORT :: Word8 -> Maybe Word32
is_TAG_STRING_SHORT :: Word8 -> Maybe Word32
is_TAG_STRING_SHORT Word8
t
  | Word8
t Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
forall a. (Eq a, Num a) => a
MASK_HIGH_NIBBLE Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x80 = Word32 -> Maybe Word32
forall a. a -> Maybe a
Just (Word32 -> Maybe Word32) -> Word32 -> Maybe Word32
forall a b. (a -> b) -> a -> b
$! Word8 -> Word32
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast (Word8
t Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
forall a. (Eq a, Num a) => a
MASK_LOW_NIBBLE)
  | Bool
otherwise                      = Maybe Word32
forall a. Maybe a
Nothing
{-# INLINE is_TAG_STRING_SHORT #-}

-- | Tag byte for string with 8-bit length (0xD0).
pattern TAG_STRING_8 :: (Eq a, Num a) => a
pattern $mTAG_STRING_8 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_STRING_8 :: forall a. (Eq a, Num a) => a
TAG_STRING_8 = 0xD0

-- | Tag byte for string with 16-bit length (0xD1).
pattern TAG_STRING_16 :: (Eq a, Num a) => a
pattern $mTAG_STRING_16 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_STRING_16 :: forall a. (Eq a, Num a) => a
TAG_STRING_16 = 0xD1

-- | Tag byte for string with 32-bit length (0xD3).
pattern TAG_STRING_32 :: (Eq a, Num a) => a
pattern $mTAG_STRING_32 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_STRING_32 :: forall a. (Eq a, Num a) => a
TAG_STRING_32 = 0xD3

-- | Base tag byte for short lists with length embedded in the low nibble (0x90).
pattern TAG_LIST_SHORT :: (Eq a, Num a) => a
pattern $mTAG_LIST_SHORT :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_LIST_SHORT :: forall a. (Eq a, Num a) => a
TAG_LIST_SHORT = 0x90

-- is_TAG_LIST_SHORT :: Word8 -> Bool
-- is_TAG_LIST_SHORT tag = tag >= 0x90 && tag <= 0x9F
-- {-# INLINE is_TAG_LIST_SHORT #-}

-- | Test whether tag is a short list; returns embedded length if so.
is_TAG_LIST_SHORT :: Word8 -> Maybe Word32
is_TAG_LIST_SHORT :: Word8 -> Maybe Word32
is_TAG_LIST_SHORT Word8
t
  | Word8
t Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
forall a. (Eq a, Num a) => a
MASK_HIGH_NIBBLE Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0x90 = Word32 -> Maybe Word32
forall a. a -> Maybe a
Just (Word32 -> Maybe Word32) -> Word32 -> Maybe Word32
forall a b. (a -> b) -> a -> b
$! Word8 -> Word32
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast (Word8
t Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
forall a. (Eq a, Num a) => a
MASK_LOW_NIBBLE)
  | Bool
otherwise                      = Maybe Word32
forall a. Maybe a
Nothing
{-# INLINE is_TAG_LIST_SHORT #-}

-- | Tag byte for list with 8-bit length (0xD4).
pattern TAG_LIST_8 :: (Eq a, Num a) => a
pattern $mTAG_LIST_8 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_LIST_8 :: forall a. (Eq a, Num a) => a
TAG_LIST_8 = 0xD4

-- | Tag byte for list with 16-bit length (0xD5).
pattern TAG_LIST_16 :: (Eq a, Num a) => a
pattern $mTAG_LIST_16 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_LIST_16 :: forall a. (Eq a, Num a) => a
TAG_LIST_16 = 0xD5

-- | Tag byte for list with 32-bit length (0xD6).
pattern TAG_LIST_32 :: (Eq a, Num a) => a
pattern $mTAG_LIST_32 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_LIST_32 :: forall a. (Eq a, Num a) => a
TAG_LIST_32 = 0xD6

-- | Base tag byte for short dictionaries with length embedded in the low nibble (0xA0).
pattern TAG_DICTIONARY_SHORT :: (Eq a, Num a) => a
pattern $mTAG_DICTIONARY_SHORT :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_DICTIONARY_SHORT :: forall a. (Eq a, Num a) => a
TAG_DICTIONARY_SHORT = 0xA0

-- is_TAG_DICTIONARY_SHORT :: Word8 -> Bool
-- is_TAG_DICTIONARY_SHORT tag = tag >= 0xA0 && tag <= 0xAF
-- {-# INLINE is_TAG_DICTIONARY_SHORT #-}

-- | Test whether tag is a short dictionary; returns embedded length if so.
is_TAG_DICTIONARY_SHORT :: Word8 -> Maybe Word32
is_TAG_DICTIONARY_SHORT :: Word8 -> Maybe Word32
is_TAG_DICTIONARY_SHORT Word8
t
  | Word8
t Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
forall a. (Eq a, Num a) => a
MASK_HIGH_NIBBLE Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0xA0 = Word32 -> Maybe Word32
forall a. a -> Maybe a
Just (Word32 -> Maybe Word32) -> Word32 -> Maybe Word32
forall a b. (a -> b) -> a -> b
$! Word8 -> Word32
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast (Word8
t Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
forall a. (Eq a, Num a) => a
MASK_LOW_NIBBLE)
  | Bool
otherwise                      = Maybe Word32
forall a. Maybe a
Nothing
{-# INLINE is_TAG_DICTIONARY_SHORT #-}


-- | Tag byte for dictionary with 8-bit length (0xD8).
pattern TAG_DICTIONARY_8 :: (Eq a, Num a) => a
pattern $mTAG_DICTIONARY_8 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_DICTIONARY_8 :: forall a. (Eq a, Num a) => a
TAG_DICTIONARY_8 = 0xD8

-- | Tag byte for dictionary with 16-bit length (0xD9).
pattern TAG_DICTIONARY_16 :: (Eq a, Num a) => a
pattern $mTAG_DICTIONARY_16 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_DICTIONARY_16 :: forall a. (Eq a, Num a) => a
TAG_DICTIONARY_16 = 0xD9

-- | Tag byte for dictionary with 32-bit length (0xDA).
pattern TAG_DICTIONARY_32 :: (Eq a, Num a) => a
pattern $mTAG_DICTIONARY_32 :: forall {r} {a}.
(Eq a, Num a) =>
a -> ((# #) -> r) -> ((# #) -> r) -> r
$bTAG_DICTIONARY_32 :: forall a. (Eq a, Num a) => a
TAG_DICTIONARY_32 = 0xDA

-- is_TAG_STRUCTURE :: Word8 -> Bool
-- is_TAG_STRUCTURE tag = tag >= 0xB0 && tag <= 0xBF
-- {-# INLINE is_TAG_STRUCTURE #-}

-- | Test whether tag is a structure marker; returns the field count if so.
is_TAG_STRUCTURE :: Word8 -> Maybe Word32
is_TAG_STRUCTURE :: Word8 -> Maybe Word32
is_TAG_STRUCTURE Word8
t
  | Word8
t Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
forall a. (Eq a, Num a) => a
MASK_HIGH_NIBBLE Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0xB0 = Word32 -> Maybe Word32
forall a. a -> Maybe a
Just (Word32 -> Maybe Word32) -> Word32 -> Maybe Word32
forall a b. (a -> b) -> a -> b
$! Word8 -> Word32
forall a b.
(Integral a, Integral b, IsIntSubType a b ~ 'True) =>
a -> b
intCast (Word8
t Word8 -> Word8 -> Word8
forall a. Bits a => a -> a -> a
.&. Word8
forall a. (Eq a, Num a) => a
MASK_LOW_NIBBLE)
  | Bool
otherwise                      = Maybe Word32
forall a. Maybe a
Nothing
{-# INLINE is_TAG_STRUCTURE #-}