{- Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of this source tree. -} module Thrift.Protocol.JSON.Base64 ( encodeBase64 , encodeBase64Text , decodeBase64 ) where import Data.Bits import Data.ByteString.Builder import Data.ByteString.Builder.Prim as Prim import Data.ByteString.Internal import Data.ByteString.Lazy (toStrict) import Data.Text (Text) import Data.Word import Data.Vector ((!)) import qualified Data.ByteString as BS import qualified Data.Text as Text import qualified Data.Vector as Vector symbol :: Word8 -> Word8 symbol w = BS.index "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" (fromIntegral w) encodeBase64 :: ByteString -> Builder encodeBase64 = encode . BS.unpack where -- case 1: len >= 3 encode (b0 : b1 : b2 : bs) = primFixed (Prim.word8 >*< Prim.word8 >*< Prim.word8 >*< Prim.word8) (symbol $ (b0 `shiftR` 2) .&. 0x3f, (symbol $ ((b0 `shiftL` 4) .&. 0x30) .|. ((b1 `shiftR` 4) .&. 0x0f), (symbol $ ((b1 `shiftL` 2) .&. 0x3c) .|. ((b2 `shiftR` 6) .&. 0x03), symbol $ b2 .&. 0x3f))) <> encode bs -- case 2: len == 2 encode [b0, b1] = primFixed (Prim.word8 >*< Prim.word8 >*< Prim.word8) (symbol $ (b0 `shiftR` 2) .&. 0x3f, (symbol $ ((b0 `shiftL` 4) .&. 0x30) .|. ((b1 `shiftR` 4) .&. 0x0f), symbol $ (b1 `shiftL` 2) .&. 0x3c)) -- case 3: len == 1 encode [b0] = primFixed (Prim.word8 >*< Prim.word8) (symbol $ (b0 `shiftR` 2) .&. 0x3f, symbol $ (b0 `shiftL` 4) .&. 0x30) -- case 4: len == 0 (impossible) encode [] = mempty encodeBase64Text :: ByteString -> Text encodeBase64Text = Text.pack . map w2c . BS.unpack . toStrict . toLazyByteString . encodeBase64 decodeTable :: Word8 -> Word8 decodeTable = (table!) . fromIntegral where table = Vector.fromList [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f , 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 , 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 , 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24 , 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30 , 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff , 0xff, 0xff, 0xff, 0xff ] decodeBase64 :: ByteString -> ByteString decodeBase64 = BS.pack . decode . BS.unpack where decode (b0 : b1 : b2 : b3 : bs) = decode0 b0 b1 : decode1 b1 b2 : decode2 b2 b3 : decode bs decode [b0, b1, b2] = [decode0 b0 b1, decode1 b1 b2] decode [b0, b1] = [decode0 b0 b1] decode _ = [] decode0 b0 b1 = (decodeTable b0 `shiftL` 2) .|. (decodeTable b1 `shiftR` 4) decode1 b1 b2 = ((decodeTable b1 `shiftL` 4) .&. 0xf0) .|. (decodeTable b2 `shiftR` 2) decode2 b2 b3 = ((decodeTable b2 `shiftL` 6) .&. 0xc0) .|. decodeTable b3