hbcd (empty) → 1.0
raw patch · 4 files changed
+149/−0 lines, 4 filesdep +Decimaldep +basedep +bytestringsetup-changed
Dependencies added: Decimal, base, bytestring, digits, split
Files
- LICENSE.txt +20/−0
- Setup.hs +5/−0
- hbcd.cabal +28/−0
- src/Data/BCD/Packed.hs +96/−0
+ LICENSE.txt view
@@ -0,0 +1,20 @@+Copyright (c) 2012 Andrew Kay++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.
+ Setup.hs view
@@ -0,0 +1,5 @@+module Main where++import Distribution.Simple++main = defaultMain
+ hbcd.cabal view
@@ -0,0 +1,28 @@+Name: hbcd+Version: 1.0+Synopsis: Packed binary-coded decimal (BCD) serialization+Description: A module containing packed binary-coded decimal (BCD) serialization+ functions. Packed BCD is commonly used to encode numbers in mainframe+ datasets.+License: MIT+License-File: LICENSE.txt+Copyright: Andrew Kay, 2012+Author: Andrew Kay+Maintainer: andrewjkay@gmail.com+Stability: provisional+Category: Data+Cabal-Version: >= 1.6+Build-Type: Simple++Library+ Exposed-Modules: Data.BCD.Packed+ HS-Source-Dirs: src+ Build-Depends: base >= 4 && < 5,+ bytestring >= 0.9.1.10,+ Decimal >= 0.2.3,+ digits >= 0.2,+ split >= 0.1.4.3++Source-Repository head+ Type: hg+ Location: https://bitbucket.org/andrewjkay/hbcd
+ src/Data/BCD/Packed.hs view
@@ -0,0 +1,96 @@+{- |+Module : Data.BCD.Packed+Copyright : Andrew Kay, 2012+License: : MIT++Maintainer : andrewjkay@gmail.com+Stability : experimental+Portability : portable++A module containing packed binary-coded decimal (BCD) serialization functions+-}++module Data.BCD.Packed (+ bytesRequired,+ packInteger,+ unpackInteger,+ packDecimal,+ unpackDecimal ) where++import Data.Word+import Data.Bits+import qualified Data.ByteString as BS+import Data.Decimal (Decimal, DecimalRaw(Decimal), decimalMantissa, decimalPlaces, roundTo)+import Data.Digits (digits)+import Data.List.Split (splitEvery)++-- | Pack two digits into a byte+packByte :: [Word8] -> Word8+packByte [m, l] = (shiftL m 4) .|. l++-- | Pack every pair of two digits into a byte resulting in a ByteString half as long as+-- the input+packBytes :: [Word8] -> BS.ByteString+packBytes bx = BS.pack $ map packByte $ splitEvery 2 bx++-- | Unpack two digits from a byte+unpackByte :: Word8 -> [Word8]+unpackByte b = [shiftR (b .&. 0xf0) 4, b .&. 0x0f]++-- | Unpack two digits from every byte in a ByteString resulting in a new ByteString twice+-- as long as the input+unpackBytes :: BS.ByteString -> BS.ByteString+unpackBytes bs = BS.concatMap (\b -> BS.pack $ unpackByte b) bs++-- | Encode the sign+encodeSign :: Integer -> Word8+encodeSign s+ | s < 0 = 0xd+ | otherwise = 0xc++-- | Decode the sign+decodeSign :: Word8 -> Integer+decodeSign n = case n of+ 0xc -> 1+ 0xd -> -1+ otherwise -> error "Unsupported sign value"++-- | Calculate the bytes required to store a number of digits+bytesRequired :: Int -- ^ Number of digits (including leading zeros)+ -> Int -- ^ Number of bytes required+bytesRequired l = ceiling (((fromIntegral l) + 1) / 2)++-- | Pack an Integer into a ByteString+packInteger :: Int -- ^ Number of digits (including leading zeros)+ -> Integer -- ^ Value + -> BS.ByteString -- ^ Packed BCD+packInteger l n+ | dsl > l = error "Number is to large for field"+ | otherwise = packBytes $ (replicate (nl - dsl - 1) 0) ++ ds ++ [s]+ where ds = map (\d -> fromIntegral d) (digits 10 (abs n))+ s = encodeSign (signum n)+ nl = (bytesRequired l) * 2+ dsl = length ds++-- | Unpack an Integer from a ByteString+unpackInteger :: BS.ByteString -- ^ Packed BCD+ -> Integer -- ^ Value+unpackInteger bs = n * s+ where ubs = unpackBytes bs+ n = BS.foldl (\n d -> (n * 10) + (toInteger d)) 0 $ BS.init ubs+ s = decodeSign $ BS.last ubs++-- | Pack a Decimal into a ByteString+packDecimal :: Int -- ^ Number of digits (including leading zeros and decimal places)+ -> Word8 -- ^ Number of decimal places+ -> Decimal -- ^ Value+ -> BS.ByteString -- ^ Packed BCD+packDecimal l d n+ | (decimalPlaces n) > d = error "Decimal places to large for field"+ | otherwise = packInteger l $ decimalMantissa $ roundTo d n++-- | Unpack a Decimal from a ByteString+unpackDecimal :: Word8 -- ^ Number of decimal places+ -> BS.ByteString -- ^ Packed BCD+ -> Decimal -- ^ Value+unpackDecimal d bs = Decimal d $ unpackInteger bs