{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE RecordWildCards #-}
module Codec.QRCode.Code.Data
( qrSize
, QRInternal
, calcVersionAndErrorLevel
, appendEndAndPadding
, appendErrorCorrection
) where
import Codec.QRCode.Base
import qualified Data.Vector.Unboxed as UV
import Codec.QRCode.Code.ReedSolomonEncoder
import qualified Codec.QRCode.Data.ByteStreamBuilder as BSB
import Codec.QRCode.Data.ErrorLevel
import Codec.QRCode.Data.Mask
import Codec.QRCode.Data.QRCodeOptions
import Codec.QRCode.Data.QRIntermediate.Internal
import Codec.QRCode.Data.QRSegment.Internal
import Codec.QRCode.Data.Result
import Codec.QRCode.Data.Version
qrSize :: Version -> Int
{-# INLINE qrSize #-}
qrSize ver = 17 + unVersion ver * 4
type QRInternal t = (Version, ErrorLevel, t, Maybe Mask)
calcVersionAndErrorLevel :: QRCodeOptions -> QRSegment -> Result QRIntermediate
calcVersionAndErrorLevel QRCodeOptions{..} input =
firstSuccess checkSizeVR [minBound .. maxBound]
where
checkSizeVR :: VersionRange -> Result QRIntermediate
checkSizeVR vr = do
let
versions = versionsInRangeLimitedBy vr qroMinVersion qroMaxVersion
guard (not (null versions))
stream <- unQRSegment input vr
firstSuccess (checkSize stream) versions
checkSize :: BSB.ByteStreamBuilder -> Version -> Result QRIntermediate
checkSize bs v = do
let
bsl = BSB.length bs
el <- firstMatch (\e -> bsl <= 8 * numDataCodeWords v e) errorLevels
pure $
QRIntermediate v el bsl bs qroMask
errorLevels :: [ErrorLevel]
errorLevels
| qroBoostErrorLevel = [H, Q .. qroErrorLevel]
| otherwise = [qroErrorLevel]
firstSuccess :: (a -> Result b) -> [a] -> Result b
firstSuccess fn = foldr ((<|>) . fn) empty
firstMatch :: (a -> Bool) -> [a] -> Result a
firstMatch fn = firstSuccess (\e -> bool empty (pure e) (fn e))
appendEndAndPadding :: QRIntermediate -> QRInternal BSB.ByteStreamBuilder
appendEndAndPadding (QRIntermediate v e bsl bs mmask) =
let
capacity = 8 * numDataCodeWords v e
endLen = 4 `min` (capacity - bsl)
pad0Len = negate (bsl + endLen) `mod` 8
padEC11Len = capacity - (bsl + endLen + pad0Len)
in
(v, e, bs <> BSB.encodeBits (endLen + pad0Len) 0 <> BSB.fromList (take (padEC11Len `div` 8) (cycle [0xec, 0x11])), mmask)
appendErrorCorrection :: QRInternal BSB.ByteStreamBuilder -> QRInternal [Word8]
appendErrorCorrection (v, e, bs, mmask) =
let
numBlocks = numErrorCorrectionBlocks v e
blockEccLen = eccCodeWordsPerBlock v e
rawCodeWords = numRawDataModules v `div` 8
numShortBlocks = numBlocks - (rawCodeWords `mod` numBlocks)
shortBlockLen = rawCodeWords `div` numBlocks
generatorPolynomial = rsGeneratorPolynomial blockEccLen
dataBlockLens = [shortBlockLen - blockEccLen + bool 0 1 (x >= numShortBlocks) | x <- [0 .. numBlocks - 1]]
dataBlocks = snd $ mapAccumL (\da len -> swap (splitAt len da)) (BSB.toList bs) dataBlockLens
eccBlocks = map (rsEncode generatorPolynomial) dataBlocks
interleaved = transpose dataBlocks ++ transpose eccBlocks
in
(v, e, concat interleaved, mmask)
numDataCodeWords :: Version -> ErrorLevel -> Int
{-# INLINABLE numDataCodeWords #-}
numDataCodeWords v e =
numRawDataModules v `div` 8
- eccCodeWordsPerBlock v e
* numErrorCorrectionBlocks v e
numRawDataModules :: Version -> Int
numRawDataModules ver =
let
size = qrSize ver
v2
| unVersion ver < 2 = 0
| otherwise =
let
numAlign = unVersion ver `div` 7 + 2
in
- (numAlign - 1) * (numAlign - 1) * 25
- (numAlign - 2) * 2 * 20
v7
| unVersion ver < 7 = 0
| otherwise = - 18 * 2
in
size * size
- 64 * 3
- (15 * 2 + 1)
- (size - 16) * 2
+ v2
+ v7
eccCodeWordsPerBlock :: Version -> ErrorLevel -> Int
{-# INLINE eccCodeWordsPerBlock #-}
eccCodeWordsPerBlock v e = eccCodeWordsPerBlockData UV.! (fromEnum e * 40 + unVersion v - 1)
eccCodeWordsPerBlockData :: UV.Vector Int
{-# NOINLINE eccCodeWordsPerBlockData #-}
eccCodeWordsPerBlockData =
[
7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30
]
numErrorCorrectionBlocks :: Version -> ErrorLevel -> Int
{-# INLINE numErrorCorrectionBlocks #-}
numErrorCorrectionBlocks v e = numErrorCorrectionBlocksData UV.! (fromEnum e * 40 + unVersion v - 1)
numErrorCorrectionBlocksData :: UV.Vector Int
{-# NOINLINE numErrorCorrectionBlocksData #-}
numErrorCorrectionBlocksData =
[
1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25,
1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49,
1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68,
1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81
]