1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2006 The Android Open Source Project
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */
8ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkBase64.h"
118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#define DecodePad -2
138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#define EncodePad 64
148a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
15d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.comstatic const char default_encode[] =
168a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
178a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    "abcdefghijklmnopqrstuvwxyz"
188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    "0123456789+/=";
198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comstatic const signed char decodeData[] = {
218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    62, -1, -1, -1, 63,
228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, DecodePad, -1, -1,
238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    -1,  0,  1,  2,  3,  4,  5,  6, 7,  8,  9, 10, 11, 12, 13, 14,
248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com};
288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
298a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comSkBase64::SkBase64() : fLength((size_t) -1), fData(NULL) {
308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#if defined _WIN32 && _MSC_VER >= 1300  // disable 'two', etc. may be used without having been initialized
338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#pragma warning ( push )
348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#pragma warning ( disable : 4701 )
358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif
368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comSkBase64::Error SkBase64::decode(const void* srcPtr, size_t size, bool writeDestination) {
388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    unsigned char* dst = (unsigned char*) fData;
398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const unsigned char* dstStart = (const unsigned char*) fData;
408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const unsigned char* src = (const unsigned char*) srcPtr;
418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    bool padTwo = false;
428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    bool padThree = false;
438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const unsigned char* end = src + size;
448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    while (src < end) {
458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        unsigned char bytes[4];
468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        int byte = 0;
478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        do {
488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            unsigned char srcByte = *src++;
498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (srcByte == 0)
508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                goto goHome;
518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (srcByte <= ' ')
528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                continue; // treat as white space
538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (srcByte < '+' || srcByte > 'z')
548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                return kBadCharError;
558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            signed char decoded = decodeData[srcByte - '+'];
568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            bytes[byte] = decoded;
578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (decoded < 0) {
58d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com                if (decoded == DecodePad)
598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                    goto handlePad;
608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                return kBadCharError;
618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            } else
628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                byte++;
638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (*src)
648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                continue;
658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (byte == 0)
668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                goto goHome;
678a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (byte == 4)
688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                break;
698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comhandlePad:
708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (byte < 2)
718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                return kPadError;
728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            padThree = true;
738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (byte == 2)
748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                padTwo = true;
758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        } while (byte < 4);
77a7ed3cc6371e50a5beb3750c475fe23665dafa7dtomhudson@google.com        int two = 0;
78a7ed3cc6371e50a5beb3750c475fe23665dafa7dtomhudson@google.com        int three = 0;
798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (writeDestination) {
808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            int one = (uint8_t) (bytes[0] << 2);
818a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            two = bytes[1];
828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            one |= two >> 4;
838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            two = (uint8_t) (two << 4);
848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            three = bytes[2];
858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            two |= three >> 2;
868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            three = (uint8_t) (three << 6);
878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            three |= bytes[3];
888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            SkASSERT(one < 256 && two < 256 && three < 256);
898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst = (unsigned char) one;
908a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
918a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst++;
92d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com        if (padTwo)
938a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
948a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (writeDestination)
958a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst = (unsigned char) two;
968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst++;
978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (padThree)
988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            break;
998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (writeDestination)
1008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst = (unsigned char) three;
1018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst++;
1028a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1038a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comgoHome:
1048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    fLength = dst - dstStart;
1058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return kNoError;
1068a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1078a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
108d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com#if defined _WIN32 && _MSC_VER >= 1300
1098a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#pragma warning ( pop )
1108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#endif
1118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
112af5bbf24498634142f4227eed2b120169a08156bbungeman@google.comsize_t SkBase64::Encode(const void* srcPtr, size_t length, void* dstPtr, const char* encodeMap) {
113af5bbf24498634142f4227eed2b120169a08156bbungeman@google.com    const char* encode;
114af5bbf24498634142f4227eed2b120169a08156bbungeman@google.com    if (NULL == encodeMap) {
115af5bbf24498634142f4227eed2b120169a08156bbungeman@google.com        encode = default_encode;
116af5bbf24498634142f4227eed2b120169a08156bbungeman@google.com    } else {
117af5bbf24498634142f4227eed2b120169a08156bbungeman@google.com        encode = encodeMap;
118af5bbf24498634142f4227eed2b120169a08156bbungeman@google.com    }
1198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const unsigned char* src = (const unsigned char*) srcPtr;
1208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    unsigned char* dst = (unsigned char*) dstPtr;
1218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (dst) {
1228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        size_t remainder = length % 3;
1238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        const unsigned char* end = &src[length - remainder];
1248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        while (src < end) {
1258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            unsigned a = *src++;
1268a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            unsigned b = *src++;
1278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            unsigned c = *src++;
1288a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            int      d = c & 0x3F;
129d6176b0dcacb124539e0cfd051e6d93a9782f020rmistry@google.com            c = (c >> 6 | b << 2) & 0x3F;
1308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            b = (b >> 4 | a << 4) & 0x3F;
1318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            a = a >> 2;
1328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst++ = encode[a];
1338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst++ = encode[b];
1348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst++ = encode[c];
1358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst++ = encode[d];
1368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
1378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (remainder > 0) {
1388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            int k1 = 0;
1398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            int k2 = EncodePad;
1408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            int a = (uint8_t) *src++;
1418a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            if (remainder == 2)
1428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            {
1438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                int b = *src++;
1448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                k1 = b >> 4;
1458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                k2 = (b << 2) & 0x3F;
1468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            }
1478a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst++ = encode[a >> 2];
1488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst++ = encode[(k1 | a << 4) & 0x3F];
1498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst++ = encode[k2];
1508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            *dst++ = encode[EncodePad];
1518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
1528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1538a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return (length + 2) / 3 * 4;
1548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
1558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
1568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.comSkBase64::Error SkBase64::decode(const char* src, size_t len) {
1578a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    Error err = decode(src, len, false);
1588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkASSERT(err == kNoError);
1598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (err != kNoError)
1608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return err;
1618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    fData = new char[fLength];  // should use sk_malloc/sk_free
1628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    decode(src, len, true);
1638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return kNoError;
1648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
165