Base85 Class

Updated tinstaafl 0 Tallied Votes 1K Views Share

Here's a simple class for what it's worth that will do simple base85 encoding and decoding. This only works with the required blocks of data and not large data structures or streams, and does no validation of the data. What it does do is encode a block of 4 8-bit bytes into 5 base85 characters and decode a block of 5 Base85 characters into 4 8-bit bytes. There's also a provision in the definition for base85 to represent 5 !'s with a single 'z'. However the definition I read wasn't clear on whether it was mandatory, so I didn't implement any checking for that.

Some may ask why one would want this. Ever tried to send an attachment through an email server that won't accept them? With this you can encode any 8-bit data to look like text and be reliably decoded on the other end.

Base85.h

#pragma once
#include<string>
#include<sstream>
#include<vector>
#include<math.h>

using namespace std;

class Base85
{
    public:
        Base85(void);
        ~Base85(void);
        vector<unsigned char> DecodeBlock(string chars);
        string EncodeBlock(vector<unsigned char> bytes, bool lastblock);
    private:
        string ToBinary(long long input, int length);
        double ToDoub(string input);
};


Base85.cpp

#include "Base85.h"

using namespace std;

Base85::Base85(void)
{
}

Base85::~Base85(void)
{
}

//Takes a vector of 4 8-bit bytes and return a string of 5-7 characters, depending on last block
string Base85::EncodeBlock(vector<unsigned char> bytes, bool lastblock)
{
    string retval = "";	
    string ss = "";
    int size = bytes.size();
    //Pad the block to 4 8-bit bytes
    for(int i = 0; i < 4 - size; i++)
    {
        bytes.push_back(0);
    }
    //convert the 4 8-bit bytes to one 32-bit binary
    for(int j = 0; j<4;j++)
    {			
        ss += ToBinary(bytes[j],8);
    }
    //Convert the 32-bit binary to to base10.
    double bintodoub = ToDoub(ss);
    //Convert the base10 to 5 digit base85.  This also adds 33 to each one, to make it a displayable character,
    //and stores it in the return string
    for(int j = 0; j < 5; j++)
    {
        retval += ((unsigned char)((long long)(bintodoub/pow(85.0,(double)4-j)) % 85) + 33);
    }
    //Add last block signifier if necessary
    retval = retval.substr(0,retval.length() - (4 - size)) + "~>";
    return retval;
}

//Takes a string of 5 characters and returns a vector of up to 4 8-bit bytes
vector<unsigned char> Base85::DecodeBlock(string chars)
{
    vector<unsigned char> retval;
    double base85todoub = 0;
    //Remove last block signifier if necessary
    if(chars[chars.length() - 1] == '>' && chars[chars.length() - 2] == '~')
        chars = chars.substr(0, chars.length() - 2);
    //Take each character subtract 33 and convert from base85 to base10
    for(int i = 0; i <4; i++)
    {
        base85todoub += (double)(chars[i]-33) * pow(85.0, (double)(4-i));
    }
    base85todoub += chars[4] - 33;
    //Convert the base 10 number to 32-bit binary
    string doubtobin = ToBinary((int)base85todoub,32);
    //Break the 32-bit binary into 8-bit binary blocks and convert each block to base10 8-bit byte
    for(int i = 0; i < 32; i+=8)
    {
        retval.push_back((unsigned char)(ToDoub(doubtobin.substr(i,8))));
    }
    return retval;
}

//Takes integer and the required length and returns a binary string representation.
//This does no checking to see if the length is long enough to adequately represent the value of the integer
string Base85::ToBinary(long long input, int length)
{
    string output = "";
    for(int i = 1; i <= length; i++)
    {
        output +=  "0";
    }
    for(int i = length - 1; i >= 0; i--)
    {
        long long test = pow(2.0,i);
        if(input >= test)
        {
            output[(length - 1)-i] = '1';
            input -= test;
        }
    
    }
    return output;
}

//Take a string representation of a binary number and return the base10 representation of it.  
//There's no validation of the string
double Base85::ToDoub(string input)
{
    int length = input.length();
    double output = 0;
    double temp = 0;
    for(int i = 0; i < length; i++)
    {
        stringstream ss;
        ss << input[(length - 1) - i];
        ss >> temp;
        output += pow(2.0,i) * temp;
    }
    return output;
}


Main.cpp

using namespace std;

Base85 b85;
vector<unsigned char> test;
string testout;

void printout()
{
    for(int i = 0; i < 4; i++)
    {
        cout << test[i] << " , ";
    }
    cout << endl;
}

int main()
{
  
    test.push_back(18);
    test.push_back(52);
    test.push_back(86);
    test.push_back(120);
    printout();
    testout = b85.EncodeBlock(test, true);
    cout << testout << '\t' << testout.length() << endl;	test.clear();
    test = b85.DecodeBlock(testout);
    printout();
    system("pause");
    return 0;
}
tinstaafl 1,176 Posting Maven

Oops, my original code has a slight bug, here's the corrected code:

#include "Base85.h"
using namespace std;
Base85::Base85(void)
{
}
Base85::~Base85(void)
{
}
//Takes a vector of 4 8-bit bytes and return a string of 4-7 characters, depending on last block
string Base85::EncodeBlock(vector<unsigned char> bytes, bool lastblock)
{
    string retval = ""; 
    string ss = "";
    int size = bytes.size();
    //Pad the block to 4 8-bit bytes
    for(int i = 0; i < 4 - size; i++)
    {
        bytes.push_back(0);
    }
    //convert the 4 8-bit bytes to one 32-bit binary
    for(int j = 0; j<4;j++)
    {           
        ss += ToBinary(bytes[j],8);
    }
    //Convert the 32-bit binary to to base10.
    double bintodoub = ToDoub(ss);
    //Convert the base10 to 5 digit base85.  This also adds 33 to each one, to make it a displayable character,
    //and stores it in the return string
    for(int j = 0; j < 5; j++)
    {
        retval += ((unsigned char)((long long)(bintodoub/pow(85.0,(double)4-j)) % 85) + 33);
    }
    //Add last block signifier if necessary
    if(lastblock)
        retval = retval.substr(0,retval.length() - (4 - size)) + "~>";
    return retval;
}
//Takes a string of 5 characters and returns a vector of up to 4 8-bit bytes
vector<unsigned char> Base85::DecodeBlock(string chars)
{
    vector<unsigned char> retval;
    double base85todoub = 0;
    //Remove last block signifier if necessary
    if(chars[chars.length() - 1] == '>' && chars[chars.length() - 2] == '~')
        chars = chars.substr(0, chars.length() - 2);
    //Take each character subtract 33 and convert from base85 to base10
    for(int i = 0; i <4; i++)
    {
        base85todoub += (double)(chars[i]-33) * pow(85.0, (double)(4-i));
    }
    base85todoub += chars[4] - 33;
    //Convert the base 10 number to 32-bit binary
    string doubtobin = ToBinary((int)base85todoub,32);
    //Break the 32-bit binary into 8-bit binary blocks and convert each block to base10 8-bit byte
    for(int i = 0; i < 32; i+=8)
    {
        retval.push_back((unsigned char)(ToDoub(doubtobin.substr(i,8))));
    }
    return retval;
}
//Takes integer and the required length and returns a binary string representation.
//This does no checking to see if the length is long enough to adequately represent the value of the integer
string Base85::ToBinary(long long input, int length)
{
    string output = "";
    for(int i = 1; i <= length; i++)
    {
        output +=  "0";
    }
    for(int i = length - 1; i >= 0; i--)
    {
        long long test = pow(2.0,i);
        if(input >= test)
        {
            output[(length - 1)-i] = '1';
            input -= test;
        }

    }
    return output;
}
//Take a string representation of a binary number and return the base10 representation of it.  
//There's no validation of the string
double Base85::ToDoub(string input)
{
    int length = input.length();
    double output = 0;
    double temp = 0;
    for(int i = 0; i < length; i++)
    {
        stringstream ss;
        ss << input[(length - 1) - i];
        ss >> temp;
        output += pow(2.0,i) * temp;
    }
    return output;
}
rubberman 1,355 Nearly a Posting Virtuoso Featured Poster

tinstaafl, you usually make great postings, and I'm sure when I have a spare 5 minutes I would agree that this is one of those! :-) In any case, it sounds useful, and I WILL test it out when I have those spare 5 minutes!

tinstaafl 1,176 Posting Maven

k thx

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.