11cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
21cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger/*
31cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Copyright 2006 The Android Open Source Project
41cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger *
51cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * Use of this source code is governed by a BSD-style license that can be
61cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger * found in the LICENSE file.
71cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger */
81cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger
90910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#include "SkBase64.h"
110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
120910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#define DecodePad -2
130910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#define EncodePad 64
140910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
154f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenbergerstatic const char default_encode[] =
160910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
170910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    "abcdefghijklmnopqrstuvwxyz"
180910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    "0123456789+/=";
190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectstatic const signed char decodeData[] = {
210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    62, -1, -1, -1, 63,
220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, DecodePad, -1, -1,
230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    -1,  0,  1,  2,  3,  4,  5,  6, 7,  8,  9, 10, 11, 12, 13, 14,
240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project};
280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source ProjectSkBase64::SkBase64() : fLength((size_t) -1), fData(NULL) {
300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#if defined _WIN32 && _MSC_VER >= 1300  // disable 'two', etc. may be used without having been initialized
330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#pragma warning ( push )
340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#pragma warning ( disable : 4701 )
350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source ProjectSkBase64::Error SkBase64::decode(const void* srcPtr, size_t size, bool writeDestination) {
380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    unsigned char* dst = (unsigned char*) fData;
390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const unsigned char* dstStart = (const unsigned char*) fData;
400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const unsigned char* src = (const unsigned char*) srcPtr;
410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool padTwo = false;
420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    bool padThree = false;
430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const unsigned char* end = src + size;
440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    while (src < end) {
450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        unsigned char bytes[4];
460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        int byte = 0;
470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        do {
480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            unsigned char srcByte = *src++;
490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (srcByte == 0)
500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                goto goHome;
510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (srcByte <= ' ')
520910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                continue; // treat as white space
530910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (srcByte < '+' || srcByte > 'z')
540910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                return kBadCharError;
550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            signed char decoded = decodeData[srcByte - '+'];
560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            bytes[byte] = decoded;
570910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (decoded < 0) {
580910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                if (decoded == DecodePad)
590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                    goto handlePad;
600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                return kBadCharError;
610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            } else
620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                byte++;
630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (*src)
640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                continue;
650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (byte == 0)
660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                goto goHome;
670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (byte == 4)
680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                break;
690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source ProjecthandlePad:
700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (byte < 2)
710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                return kPadError;
720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            padThree = true;
730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (byte == 2)
740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                padTwo = true;
750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            break;
760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        } while (byte < 4);
771cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        int two = 0;
781cab2921ab279367f8206cdadc9259d12e603548Derek Sollenberger        int three = 0;
790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (writeDestination) {
800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            int one = (uint8_t) (bytes[0] << 2);
810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            two = bytes[1];
820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            one |= two >> 4;
830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            two = (uint8_t) (two << 4);
840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            three = bytes[2];
850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            two |= three >> 2;
860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            three = (uint8_t) (three << 6);
870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            three |= bytes[3];
880910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            SkASSERT(one < 256 && two < 256 && three < 256);
890910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst = (unsigned char) one;
900910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
910910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        dst++;
920910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (padTwo)
930910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            break;
940910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (writeDestination)
950910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst = (unsigned char) two;
960910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        dst++;
970910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (padThree)
980910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            break;
990910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (writeDestination)
1000910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst = (unsigned char) three;
1010910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        dst++;
1020910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
1030910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source ProjectgoHome:
1040910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    fLength = dst - dstStart;
1050910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return kNoError;
1060910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1070910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1080910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#if defined _WIN32 && _MSC_VER >= 1300
1090910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#pragma warning ( pop )
1100910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
1110910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1124f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenbergersize_t SkBase64::Encode(const void* srcPtr, size_t length, void* dstPtr, const char* encodeMap) {
1134f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger    const char* encode;
1144f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger    if (NULL == encodeMap) {
1154f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger        encode = default_encode;
1164f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger    } else {
1174f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger        encode = encodeMap;
1184f1dae40e24d57d647db01443b8bf2410514b8b5Derek Sollenberger    }
1190910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    const unsigned char* src = (const unsigned char*) srcPtr;
1200910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    unsigned char* dst = (unsigned char*) dstPtr;
1210910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (dst) {
1220910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        size_t remainder = length % 3;
1230910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        const unsigned char* end = &src[length - remainder];
1240910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        while (src < end) {
1250910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            unsigned a = *src++;
1260910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            unsigned b = *src++;
1270910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            unsigned c = *src++;
1280910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            int      d = c & 0x3F;
1290910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            c = (c >> 6 | b << 2) & 0x3F;
1300910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            b = (b >> 4 | a << 4) & 0x3F;
1310910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            a = a >> 2;
1320910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst++ = encode[a];
1330910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst++ = encode[b];
1340910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst++ = encode[c];
1350910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst++ = encode[d];
1360910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
1370910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        if (remainder > 0) {
1380910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            int k1 = 0;
1390910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            int k2 = EncodePad;
1400910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            int a = (uint8_t) *src++;
1410910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            if (remainder == 2)
1420910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            {
1430910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                int b = *src++;
1440910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                k1 = b >> 4;
1450910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project                k2 = (b << 2) & 0x3F;
1460910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            }
1470910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst++ = encode[a >> 2];
1480910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst++ = encode[(k1 | a << 4) & 0x3F];
1490910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst++ = encode[k2];
1500910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project            *dst++ = encode[EncodePad];
1510910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        }
1520910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
1530910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return (length + 2) / 3 * 4;
1540910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1550910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1560910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source ProjectSkBase64::Error SkBase64::decode(const char* src, size_t len) {
1570910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    Error err = decode(src, len, false);
1580910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    SkASSERT(err == kNoError);
1590910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    if (err != kNoError)
1600910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        return err;
1610910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    fData = new char[fLength];  // should use sk_malloc/sk_free
1620910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    decode(src, len, true);
1630910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    return kNoError;
1640910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1650910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1660910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#ifdef SK_SUPPORT_UNITTEST
1670910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Projectvoid SkBase64::UnitTest() {
1680910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    signed char all[256];
1690910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int index = 0; index < 256; index++)
1700910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        all[index] = (signed char) (index + 1);
1710910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    for (int offset = 0; offset < 6; offset++) {
1720910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        size_t length = 256 - offset;
1730910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        size_t encodeLength = Encode(all + offset, length, NULL);
1740910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        char* src = (char*)sk_malloc_throw(encodeLength + 1);
1750910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        Encode(all + offset, length, src);
1760910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        src[encodeLength] = '\0';
1770910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkBase64 tryMe;
1780910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        tryMe.decode(src, encodeLength);
1790910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkASSERT(length == tryMe.fLength);
1800910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        SkASSERT(strcmp((const char*) (all + offset), tryMe.fData) == 0);
1810910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        sk_free(src);
1820910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project        delete[] tryMe.fData;
1830910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project    }
1840910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project}
1850910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project#endif
1860910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
1870910916c0f7b951ee55c4b7c6358295b9bca0565The Android Open Source Project
188