root/src/N0SlibUtils.cpp

////////////////////////////////////////////////////////////////////////
// Copyright (c) Nehmulos 2011-2014
// This file is part of N0 Strain Serialization Library.
//
// N0Strain-Serialization-Library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// N0Strain-Serialization-Library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with N0Strain-Serialization-Library.  If not, see https://gnu.org/licenses/lgpl-3.0
////////////////////////////////////////////////////////////////////////




#include "N0SlibUtils.h"
#include <string.h>

/// Contains helpful functions to interact with the filesystem.
/// And converting binary data into human readable data.
namespace nw {
namespace utils {


/// Returns true if a file exists on the filesystem.
bool fileExists(const char *filename)
{
        std::fstream file;
        file.open(filename, std::ios::in);
        if(file.is_open())
        {
                file.close();
                return true;
        }
        return false;
}

bool touchFile(const char* filename)
{
        FILE *file = fopen(filename, "a+");
        if(file != NULL)
        {
                fclose(file);
                return true;
        }
        return false;
}

bool createDirectory(const char* directory, unsigned int permissions)
{
        //TODO parse permissions and map them to the system formats
        #ifdef N0ware_System_Unix
                permissions = ALLPERMS;
                return mkdir(directory, permissions) == 0;
        #elif defined N0ware_System_Windows
                return CreateDirectory(directory, NULL);
        #else
                #warning "cannot create directories on this platform."
                return false;
        #endif
}

String getHomeDirectory()
{
        #ifdef N0ware_System_Unix
                struct passwd *pw = getpwuid(getuid());
                return pw->pw_dir;
        #elif defined N0ware_System_Windows
                static const int bufferSize = 300;
                char homeDir[bufferSize];
                ExpandEnvironmentStrings("%userprofile%", homeDir, bufferSize);
                return homeDir;
        #endif
}

/// Converts a binary blob into a human readable hex String.
String toHex(const char* blob, unsigned int size)
{
        //TODO add an encoding tag to blobs
        const char* hexTable = "0123456789ABCDEF";
        std::stringstream ss;
        for (unsigned int i = 0; i < size; ++i) {
                char hex1, hex2;
                char c = *(blob + i);
                hex1 = hexTable[(c & 0x0F)];
                hex2 = hexTable[(c & 0xF0) >> 4];
                ss << hex1;
                ss << hex2;
        }
        return ss.str();
}

String toHex(String value)
{
        return toHex(value.data(), value.size());
}

String fromHex(String hexString)
{
    static const char w = -1;
        static const int hexToValueTable[] = {
                         w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,
                         w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,
                         w,  w,  w,  w,  w,  w,  w,  w,  w,  w, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,  w,  w,
                         w,  w,  w,  w,  w, 10, 11, 12, 13, 14, 15,  w,  w,  w,  w,  w,  w,  w,  w,
                         w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,
                         w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,
                         w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w
        };
        std::stringstream ss;
        // Two HEX chars make 1 binary byte
        for (int i = 0; i < hexString.size(); ++i)
        {
                char binC = 0;
                char hex;
                // HEXchar 1
                if(hexString.at(i) > 0)
                {
                        hex = hexToValueTable[hexString.at(i)];
                        if(hex !=  w)
                                binC += hex;
                        else
                                std::cout << "Invalid hex char" << std::endl;
                }

                ++i;
                // HEXchar 2
                if(i >  w)
                {
                        if(hexString.at(i) > 0)
                        {
                                hex = hexToValueTable[hexString.at(i)] << 4;
                                if(hex !=  w)
                                        binC += hex;
                                else
                                        std::cout << "Invalid hex char" << std::endl;
                        }
                }
                ss << binC;
        }

        return ss.str();
}

// https://stackoverflow.com/a/19968992
// I already wrote an utf8 to unicode converter a few years ago,
// and I wasn't too excited to do it again the other way around.
String utf8FromIntCode(unsigned int code)
{
    std::string out;
    if (code <= 0x7f)
    {
        out.append(1, static_cast<char>(code));
    }
    else if (code <= 0x7ff)
    {
        out.append(1, static_cast<char>(0xc0 | ((code >> 6) & 0x1f)));
        out.append(1, static_cast<char>(0x80 | (code & 0x3f)));
    }
    else if (code <= 0xffff)
    {
        out.append(1, static_cast<char>(0xe0 | ((code >> 12) & 0x0f)));
        out.append(1, static_cast<char>(0x80 | ((code >> 6) & 0x3f)));
        out.append(1, static_cast<char>(0x80 | (code & 0x3f)));
    }
    else
    {
        out.append(1, static_cast<char>(0xf0 | ((code >> 18) & 0x07)));
        out.append(1, static_cast<char>(0x80 | ((code >> 12) & 0x3f)));
        out.append(1, static_cast<char>(0x80 | ((code >> 6) & 0x3f)));
        out.append(1, static_cast<char>(0x80 | (code & 0x3f)));
    }
    return out;
}

String toBase64(const char* blob, unsigned int size)
{
        static const char* base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        std::stringstream ss;
        char block[3];
        for(unsigned int i=0; i < size; )
        {
                char nulled = 0;
                //Get 24bit block from string
                for(unsigned int j=0; j < 3; ++j)
                {
                        if(i < size)
                        {
                                block[j] = blob[i];
                        }
                        else
                        {
                                block[j] = 0;
                                nulled++;
                        }
                        i++;
                }

                // Example: encoding FF FF FF to base64
                // convert 3 bytes to 4 6bit values
                //1111 11 11  1111 1111  11 11 1111          //bin
                //\-----/ \------/ \------/ \-----/          //6bit blocks
                //   |        \        \       \             //connection
                //   |         \        ''\     '''''\       //
                //0011 1111  0011 1111  0011 1111  0011 1111 //base64 in 8bit
                // Result: 3F 3F 3F 3F

                // 0xFC == 1111 1100
                // 0x03 == 0000 0011
                // 0xF0 == 1111 0000
                // 0x0F == 0000 1111
                // 0xC0 == 1100 0000
                // 0x3F == 0011 1111

                char base64Result[4]; //Contains the ids for the base64Table
                base64Result[0] = (block[0] & 0xFC) >> 2;
                base64Result[1] = ((block[0] & 0x03) << 4) + ((block[1] & 0xF0) >> 4);
                base64Result[2] = ((block[1] & 0x0F) << 2) + ((block[2] & 0xC0) >> 6);
                base64Result[3] =   block[2] & 0x3F;

                for (int j = 0; j < 4; ++j)
                {
                        if(j < 4 - nulled)
                                ss << base64Table[base64Result[j]];
                        else
                                ss << '=';
                }
        }

        return ss.str();
}

String toBase64(String value)
{
        return toBase64(value.data(), value.size());
}

String fromBase64(String encodedString)
{
        //Maps the chars to their corresponding base64 values between 0 and 63
        //Other ascii characters are mapped to  -1
        static const char w = -1;
        static const char base64ToBinaryTable[128] = {
                  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,
                  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,  w,
                  w,  w,  w,  w,  w, 62,  w,  w,  w, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
                 61,  w,  w,  w,  w,  w,  w,  w,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,
                 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  w,  w,  w,  w,
                  w,  w, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
                 43, 44, 45, 46, 47, 48, 49, 50, 51,  w,  w,  w,  w,  w
        };

        std::stringstream ss;
        for (unsigned int i = 0; i < encodedString.size(); )
        {
                char binaryTranslation[4]; // a Base64 block that represents 3 bytes
                char nulled = 0;
                unsigned int sizeOfEncodedString = encodedString.size();
                //Read 4 characters from the base64 String
                for (int j = 0; j < 4; ++j)
                {
                        if(i < sizeOfEncodedString)
                        {
                                char c = encodedString.at(i);
                                if(c != '=')
                                {
                                        //read the next char if the current is markup or invalid
                                        if(c == '\n')
                                        {
                                                --j;
                                        }
                                        else
                                        {
                                                // check for buffer overflow
                                                if(c >= 0)
                                                {
                                                        //translate the b64 char to a binary value
                                                        binaryTranslation[j] = base64ToBinaryTable[c];
                                                }
                                                else
                                                {
                                                        binaryTranslation[j] =  w;
                                                }
                                                // check for invalid character
                                                if(binaryTranslation[j] ==  w)
                                                {
                                                        --j;
                                                        std::cout << "Warning: " << c << '('<< (int)c << ')' << " is not a valid Base64 Character" << std::endl;
                                                }

                                        }
                                }
                                else
                                {
                                        binaryTranslation[j] = 0;
                                        nulled++;
                                }
                                i++;
                        }
                }
                char block[3];

                // Example: encoding 3F 3F 3F 3F to binary
                // convert 3 bytes to 4 6bit values
                //
                //0011 1111  0011 1111  0011 1111  0011 1111   //base64 in 8bit
                //  \-----/    \/ \--/    \---/\/    \-----/   //8bit blocks
                //    \        /    \       /   \       /      //connection
                //     |'''''''      |''''''     ''''''|       //
                //1111 1111     1111 1111            1111 1111 //bin
                //
                //p == skip, r == read
                //the first 2 bits of 3F don't have to be choped before addition, as they are already 0
                //2p 6r 2p 2r || 4r 2p 4r || 2r 2p 6r

                block[0] = ((binaryTranslation[0] & 0x3F) << 2) + ((binaryTranslation[1] & 0x30) >> 4);
                block[1] = ((binaryTranslation[1] & 0x0F) << 4) + ((binaryTranslation[2] & 0x3C) >> 2);
                block[2] = ((binaryTranslation[2] & 0x03) << 6) + ((binaryTranslation[3] & 0x3F));

                for (int j = 0; j < 3 - nulled; ++j)
                        ss << block[j];
        }
        return ss.str();
}

int lengthOfCharOrString(const char* str) {
        return strlen(str);
}
int lengthOfCharOrString(const char) {
        return sizeof(char);
}
int lengthOfCharOrString(const String& str) {
        return str.length();
}


}  // namespace utils
}  // namespace nw