packages feed

circe (empty) → 0.1.0.0

raw patch · 16 files changed

+572/−0 lines, 16 filesdep +basedep +bytestringdep +circe

Dependencies added: base, bytestring, circe, optparse-applicative, tasty, tasty-hunit, vector

Files

+ CHANGELOG.md view
@@ -0,0 +1,7 @@+# Revision history for circe++## 0.1.0.0 -- 8-12-26++* First version. Released on an unsuspecting world.+* CRC16 and CRC32 support+* CLI frontend
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2026 dopamane++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.
+ README.md view
@@ -0,0 +1,24 @@+# CIRCE :leopard:++CRC cyclic redundancy check++Support CRC16 & CRC32++Usage++```+crc 16 --help                  # crc options+crc 32 --help+echo -n "hello world" | crc 16 # stream stdin+crc 16 100gb.bin               # stream file+```++Development++```+cabal build+cabal haddock+cabal run test+cabal run crc+cabal install+```
+ circe.cabal view
@@ -0,0 +1,66 @@+cabal-version:      3.0+name:               circe+version:            0.1.0.0+synopsis:           CRC+description:        Cyclic redundancy check CRC16 CRC32+homepage:           https://github.com/dopamane/circe+license:            MIT+license-file:       LICENSE+author:             dopamane+maintainer:         dwc1295@gmail.com+copyright:          David Cox+category:           Codec+build-type:         Simple+extra-doc-files:    CHANGELOG.md+                    README.md++source-repository head+  type:     git+  location: https://github.com/dopamane/circe++library+  default-language: Haskell2010+  ghc-options:      -Wall -Wunused-packages -O2+  hs-source-dirs:   lib+  exposed-modules:+    Codec.Circe,+    Codec.Circe.CRC16,+    Codec.Circe.CRC32,+    Codec.Circe.Cfg,+    Codec.Circe.Pretty,+    Codec.Circe.Reflect+  build-depends:+    base < 5,+    bytestring,+    vector++executable crc+  default-language: Haskell2010+  ghc-options:      -Wall -O2 -threaded -with-rtsopts=-N+  hs-source-dirs:   exe+  main-is:          Main.hs+  other-modules:    Paths_circe+  autogen-modules:  Paths_circe+  build-depends:+    base,+    bytestring,+    circe,+    optparse-applicative,++test-suite test+  default-language: Haskell2010+  ghc-options:      -Wall -Wunused-packages -O2 -threaded+  hs-source-dirs:   test+  type:             exitcode-stdio-1.0+  main-is:          Main.hs+  other-modules:+    Test.Circe+    Test.Circe.CRC16+    Test.Circe.CRC32+    Test.Circe.Reflect+  build-depends:+    base,+    circe,+    tasty,+    tasty-hunit,+    vector
+ exe/Main.hs view
@@ -0,0 +1,115 @@+module Main (main) where++import Codec.Circe.CRC16+import Codec.Circe.CRC32+import Codec.Circe.Cfg+import Codec.Circe.Pretty+import qualified Data.ByteString.Lazy as BS+import Data.Version+import Data.Word+import Options.Applicative+import Paths_circe++main :: IO ()+main = do+  cli <- circeCLI $ showVersion version+  case cli of+    CRC16Table p -> putStrLn $ table $ w16 <$> crc16Table p+    CRC32Table p -> putStrLn $ table $ w32 <$> crc32Table p+    CRC16 cfg (Just f) -> putStrLn . w16 . crc16 cfg =<< BS.readFile f+    CRC16 cfg Nothing  -> putStrLn . w16 . crc16 cfg =<< BS.getContents+    CRC32 cfg (Just f) -> putStrLn . w32 . crc32 cfg =<< BS.readFile f+    CRC32 cfg Nothing  -> putStrLn . w32 . crc32 cfg =<< BS.getContents++data CirceCLI+  = CRC16 CRC16Cfg (Maybe String)+  | CRC32 CRC32Cfg (Maybe String)+  | -- | poly+    CRC16Table Word16+  | CRC32Table Word32++-- | run CLI, specify version string+circeCLI :: String -> IO CirceCLI+circeCLI = customExecParser prefs' . pinfo++prefs' :: ParserPrefs+prefs' = prefs $ showHelpOnError <> showHelpOnEmpty++pinfo :: String -> ParserInfo CirceCLI+pinfo vStr = info (parser <**> simpleVersioner vStr <**> helper) $+  progDesc "CIRCE - cyclic redundancy check"++parser :: Parser CirceCLI+parser = hsubparser $ mconcat+  [ command "16" $ info crc16Parser $ progDesc "CRC16"+  , command "32" $ info crc32Parser $ progDesc "CRC32"+  ]++crc16Parser :: Parser CirceCLI+crc16Parser = asum+  [ CRC16Table <$> tableOpt 16+  , CRC16 <$> crc16CfgParser <*> optional fileArg+  ]++crc32Parser :: Parser CirceCLI+crc32Parser = asum+  [ CRC32Table <$> tableOpt 32+  , CRC32 <$> crc32CfgParser <*> optional fileArg+  ]++tableOpt :: (Num a, Read a) => Int -> Parser a+tableOpt n = option auto $ mconcat+  [ short 't' <> long "table" <> metavar "POLY"+  , help $ "Generate CRC" <> show n <> " lookup table with polynomial"+  ]++crc16CfgParser :: Parser CRC16Cfg+crc16CfgParser = asum+  [ flag' crc16CCITZero $ long "ccit-zero"+  , flag' crc16Modbus $ long "modbus"+  , CRCCfg+      <$> polyOpt+      <*> initOpt+      <*> refinSwitch+      <*> refoutSwitch+      <*> finXorOpt+  ]++crc32CfgParser :: Parser CRC32Cfg+crc32CfgParser = asum+  [ CRCCfg+      <$> option auto (short 'p' <> long "poly" <> metavar "WORD32" <> help "polynomial")+      <*> option auto (short 'i' <> long "init" <> metavar "WORD32" <> help "initial value")+      <*> refinSwitch+      <*> refoutSwitch+      <*> option auto (short 'x' <> long "xor" <> metavar "WORD32" <> help "final xor")+  , flag crc32IEEE crc32IEEE $ long "ieee"+      <> help ("Default CRC32 config "+                 <> "POLY=0x4C11DB7 INIT=0xFFFFFFFF REFL-IN REFL-OUT FIN-XOR=0xFFFFFFFF")+  ]++polyOpt :: Parser Word16+polyOpt = option auto $+  short 'p' <> long "poly" <> metavar "WORD16" <> help "polynomial"+    <> value 0x1021 <> showDefaultWith w16++initOpt :: Parser Word16+initOpt = option auto $+  short 'i' <> long "init" <> metavar "WORD16" <> help "inital value"+    <> value 0 <> showDefaultWith w16++refinSwitch :: Parser Bool+refinSwitch = switch $ long "ri" <> long "refin" <> help "reflect input"++refoutSwitch :: Parser Bool+refoutSwitch = switch $ long "ro" <> long "refout" <> help "reflect output"++finXorOpt :: Parser Word16+finXorOpt = option auto $+  short 'x' <> long "xor" <> metavar "WORD16"+    <> value 0 <> showDefaultWith w16 <> help "final xor"++fileArg :: Parser String+fileArg = strArgument $+  metavar "FILE" <> completer (bashCompleter "file")+    <> help "Optional binary input file otherwise stream STDIN."
+ lib/Codec/Circe.hs view
@@ -0,0 +1,20 @@+-- | CRC high-level utilities.+-- See "Codec.Circe.CRC16" and "Codec.Circe.CRC32" for low-level functions.+module Codec.Circe+  ( -- ** CRC+    crc16+  , crc32+  , -- ** CONFIG+    CRCCfg(..)+  , -- **** CRC16+    CRC16Cfg+  , crc16CCITZero+  , crc16Modbus+  , -- **** CRC32+    CRC32Cfg+  , crc32IEEE+  ) where++import Codec.Circe.CRC16+import Codec.Circe.CRC32+import Codec.Circe.Cfg
+ lib/Codec/Circe/CRC16.hs view
@@ -0,0 +1,48 @@+-- | CRC16 utilities+module Codec.Circe.CRC16+  ( crc16+  , crc16WithTable+  , crc16Unsafe+  , crc16Table+  ) where++import Codec.Circe.Cfg+import Codec.Circe.Reflect+import Data.Bits+import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as BS+import Data.Vector.Storable (Vector)+import qualified Data.Vector.Storable as V+import Data.Function+import Data.Monoid+import Data.Semigroup+import Data.Word++-- | calculate CRC16 using lookup table generated from config+crc16 :: CRC16Cfg -> ByteString -> Word16+crc16 cfg = crc16WithTable t cfg+  where+    t = V.fromList $ crc16Table $ crcPoly cfg++-- | calculate CRC16 with precalculated poly table+crc16WithTable :: Vector Word16 -> CRC16Cfg -> ByteString -> Word16+crc16WithTable t cfg =+  xor (crcFinXor cfg)+    . applyWhen (crcRefOut cfg) ref16+    . crc16Unsafe t (crcRefIn cfg) (crcInit cfg)++-- | calculate CRC16 using an efficient lookup table and init value+crc16Unsafe :: Vector Word16 -> Bool -> Word16 -> ByteString -> Word16+crc16Unsafe t refIn = BS.foldl' go+  where+    go crc b = crc `shiftL` 8 `xor` t `V.unsafeIndex` fromIntegral pos+      where+        pos = fromIntegral (crc `shiftR` 8) `xor` applyWhen refIn ref8 b++-- | generate lookup table using polynomial+crc16Table :: Word16 -> [Word16]+crc16Table poly = calc <$> [0..255]+  where+    calc = appEndo (stimes (8 :: Int) $ Endo step) . (`shiftL` 8)+      where+        step curByte = applyWhen (testBit curByte 15) (`xor` poly) $ curByte `shiftL` 1
+ lib/Codec/Circe/CRC32.hs view
@@ -0,0 +1,50 @@+-- | CRC32 utilities+module Codec.Circe.CRC32+  ( crc32+  , crc32WithTable+  , crc32Unsafe+  , crc32Table+  ) where++import Codec.Circe.Cfg+import Codec.Circe.Reflect+import Data.Bits+import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy as BS+import Data.Vector.Storable (Vector)+import qualified Data.Vector.Storable as V+import Data.Function+import Data.Monoid+import Data.Semigroup+import Data.Word++-- | calculate CRC32 using lookup table generated from config+crc32 :: CRC32Cfg -> ByteString -> Word32+crc32 cfg = crc32WithTable t cfg+  where+    t = V.fromList $ crc32Table $ crcPoly cfg++-- | calculate CRC32 with precalculated poly table.+crc32WithTable :: Vector Word32 -> CRC32Cfg -> ByteString -> Word32+crc32WithTable t cfg =+  xor (crcFinXor cfg)+    . applyWhen (crcRefOut cfg) ref32+    . crc32Unsafe t (crcRefIn cfg) (crcInit cfg)++-- | calculate CRC32 using an efficient lookup table and init value+crc32Unsafe :: Vector Word32 -> Bool -> Word32 -> ByteString -> Word32+crc32Unsafe t refIn = BS.foldl' go+  where+    go crc b = crc `shiftL` 8 `xor` t `V.unsafeIndex` fromIntegral pos+      where+        pos = fromIntegral ((crc `xor` (fromIntegral b' `shiftL` 24)) `shiftR` 24) :: Word8+          where+            b' = applyWhen refIn ref8 b++-- | generate lookup table using polynomial+crc32Table :: Word32 -> [Word32]+crc32Table poly = calc <$> [0..255]+  where+    calc = appEndo (stimes (8 :: Int) $ Endo step) . (`shiftL` 24)+      where+        step curByte = applyWhen (testBit curByte 31) (`xor` poly) $ curByte `shiftL` 1
+ lib/Codec/Circe/Cfg.hs view
@@ -0,0 +1,43 @@+-- | CRC configurations+module Codec.Circe.Cfg+  ( CRCCfg(..)+  , CRC16Cfg+  , crc16CCITZero+  , crc16Modbus+  , CRC32Cfg+  , crc32IEEE+  ) where++import Data.Word++-- | CRC configuration parameters+data CRCCfg a = CRCCfg+  { crcPoly :: a+    -- ^ polynomial+  , crcInit :: a+    -- ^ initial value+  , crcRefIn :: Bool+    -- ^ reflect input+  , crcRefOut :: Bool+    -- ^ reflect output+  , crcFinXor :: a+    -- ^ final xor+  }+  deriving (Eq, Read, Show)++-- | specialized CRC for 'Word16'+type CRC16Cfg = CRCCfg Word16+-- | specialized CRC for 'Word32'+type CRC32Cfg = CRCCfg Word32++-- | POLY 0x1021 INIT 0x0000 NOREFL NOXOR+crc16CCITZero :: CRC16Cfg+crc16CCITZero = CRCCfg 0x1021 0x0000 False False 0x0000++-- | POLY 0x8005 INIT 0XFFFF REFLINOUT NOXOR+crc16Modbus :: CRC16Cfg+crc16Modbus = CRCCfg 0x8005 0xFFFF True True 0x0000++-- | POLY 0x4C11DB7 INIT 0xFFFFFFFF REFLINOUT XOR 0xFFFFFFFF+crc32IEEE :: CRC32Cfg+crc32IEEE = CRCCfg 0x4C11DB7 0xFFFFFFFF True True 0xFFFFFFFF
+ lib/Codec/Circe/Pretty.hs view
@@ -0,0 +1,41 @@+-- | prettyprinting utilities+module Codec.Circe.Pretty+  ( w16+  , w32+  , table+  ) where++import Data.Bits+import Data.Word+import Numeric++-- | @0x01ab@+w16 :: Word16 -> String+w16 w = "0x" ++ w16' w++-- | pretty word16 without 0x prefix+w16' :: Word16 -> String+w16' w = w3 ++ w2 ++ w1 ++ w0+  where+    w3 = showHex (w `shiftR` 12) ""+    w2 = showHex (w `shiftR` 8 .&. 0xF) ""+    w1 = showHex (w `shiftR` 4 .&. 0xF) ""+    w0 = showHex (w .&. 0xF) ""++-- | @0x0123abcd@+w32 :: Word32 -> String+w32 w = "0x" ++ w16' h ++ w16' l+  where+    h = fromIntegral $ w `shiftR` 16+    l = fromIntegral w++-- | prettyprint 8 cols across+table :: [String] -> String+table = unlines . map unwords . chunks 8++-- | chunk a list+chunks :: Int -> [a] -> [[a]]+chunks _ [] = []+chunks n xs = ys : chunks n zs+  where+    (ys, zs) = splitAt n xs
+ lib/Codec/Circe/Reflect.hs view
@@ -0,0 +1,32 @@+-- | Reflect bits+module Codec.Circe.Reflect (ref32, ref16, ref8) where++import Data.Bits+import Data.Word++-- | Reflect 'Word32'+ref32 :: Word32 -> Word32+ref32 = s1 . s2 . s4 . s8 . s16+  where+    s1 n = ((n .&. 0xAAAAAAAA) `shiftR` 1) .|. ((n .&. 0x55555555) `shiftL` 1)+    s2 n = ((n .&. 0xCCCCCCCC) `shiftR` 2) .|. ((n .&. 0x33333333) `shiftL` 2)+    s4 n = ((n .&. 0xF0F0F0F0) `shiftR` 4) .|. ((n .&. 0x0F0F0F0F) `shiftL` 4)+    s8 n = ((n .&. 0xFF00FF00) `shiftR` 8) .|. ((n .&. 0x00FF00FF) `shiftL` 8)+    s16 n = (n `shiftR` 16) .|. (n `shiftL` 16)++-- | Reflect 'Word16'+ref16 :: Word16 -> Word16+ref16 = s1 . s2 . s4 . s8+  where+    s1 n = ((n .&. 0xAAAA) `shiftR` 1) .|. ((n .&. 0x5555) `shiftL` 1)+    s2 n = ((n .&. 0xCCCC) `shiftR` 2) .|. ((n .&. 0x3333) `shiftL` 2)+    s4 n = ((n .&. 0xF0F0) `shiftR` 4) .|. ((n .&. 0x0F0F) `shiftL` 4)+    s8 n = (n `shiftR` 8) .|. (n `shiftL` 8)++-- | Reflect 'Word8'+ref8 :: Word8 -> Word8+ref8 = s1 . s2 . s4+  where+    s1 n = ((n .&. 0xAA) `shiftR` 1) .|. ((n .&. 0x55) `shiftL` 1)+    s2 n = ((n .&. 0xCC) `shiftR` 2) .|. ((n .&. 0x33) `shiftL` 2)+    s4 n = (n `shiftR` 4) .|. (n `shiftL` 4)
+ test/Main.hs view
@@ -0,0 +1,6 @@+module Main (main) where++import Test.Circe++main :: IO ()+main = testCirce
+ test/Test/Circe.hs view
@@ -0,0 +1,16 @@+module Test.Circe (testCirce) where++import qualified Test.Circe.CRC16 as CRC16+import qualified Test.Circe.CRC32 as CRC32+import qualified Test.Circe.Reflect as Reflect+import Test.Tasty++testCirce :: IO ()+testCirce = defaultMain tests++tests :: TestTree+tests = testGroup "Test.Circe"+  [ CRC16.tests+  , CRC32.tests+  , Reflect.tests+  ]
+ test/Test/Circe/CRC16.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Circe.CRC16 (tests) where++import Codec.Circe.CRC16+import Codec.Circe.Cfg+--import Data.Vector.Storable (Vector)+import qualified Data.Vector.Storable as V+import Test.Tasty+import Test.Tasty.HUnit++tests :: TestTree+tests = testGroup "CRC16"+  [ crc16UnsafeTest+  , crc16Test+  ]++crc16UnsafeTest :: TestTree+crc16UnsafeTest = testCase "crc16Unsafe" $+  crc16Unsafe (V.fromList t) False 0 "bosscoxwuzhere" @?= 0xbe42+  where+    t = crc16Table 0x1021++crc16Test :: TestTree+crc16Test = testCaseSteps "crc16" $ \step -> do+  step "modbus"+  crc16 crc16Modbus "hello world" @?= 0xddc7
+ test/Test/Circe/CRC32.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE OverloadedStrings #-}++module Test.Circe.CRC32 (tests) where++import Codec.Circe.CRC32+import Codec.Circe.Cfg+import qualified Data.Vector.Storable as V+import Test.Tasty+import Test.Tasty.HUnit++tests :: TestTree+tests = testGroup "CRC32"+  [ crc32UnsafeTest+  , crc32Test+  ]++crc32UnsafeTest :: TestTree+crc32UnsafeTest = testCase "crc32Unsafe" $+  crc32Unsafe (V.fromList t) False 0 "hello world" @?= 0x737af2ae+  where+    t = crc32Table 0x4c11db7++crc32Test :: TestTree+crc32Test = testCase "crc32" $ do+  crc32 crc32IEEE "bo$$ cox rulez" @?= 0x75ce60a3+  crc32 crc32IEEE "hello world"    @?= 0x0d4a1185+  crc32 crc32IEEE "YOLO DOLO"      @?= 0xa13ee2ed
+ test/Test/Circe/Reflect.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE BinaryLiterals     #-}+{-# LANGUAGE NumericUnderscores #-}++module Test.Circe.Reflect (tests) where++import Codec.Circe.Reflect+import Test.Tasty+import Test.Tasty.HUnit++tests :: TestTree+tests = testGroup "Reflect"+  [ ref8Test+  , ref16Test+  , ref32Test+  ]++ref8Test :: TestTree+ref8Test = testCase "ref8" $+  ref8 0b1011_1110+   @?= 0b0111_1101++ref16Test :: TestTree+ref16Test = testCase "ref16" $+  ref16 0b1010_1111_0000_0011+    @?= 0b1100_0000_1111_0101++ref32Test :: TestTree+ref32Test = testCase "ref32" $+  ref32 0b1110_0111_0000_0000_1111_1111_1010_1010+    @?= 0b0101_0101_1111_1111_0000_0000_1110_0111