C/C++学习笔记 dlib中的base64编码源码分析

2024-03-06 10:59

本文主要是介绍C/C++学习笔记 dlib中的base64编码源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、头文件解析

        iostream标准输入输出流

        sstream 字符串输入输出流

        头文件climits定义了符号常量来表示类型的限制,编译器厂商提供了climits文件,该文件支持了其编译器中的值。例如,在使用16位int的老系统中,climits文件将INT_MAX定义为32676。

#include "base64_kernel_1.h"
#include <iostream>
#include <sstream>
#include <climits>

         fstream 文件输入输出流

二、换行符CR,LF和CRLF

        CR:\r 表示回车,MacIntosh操作系统(即早期的Mac操作系统)采用单个字符CR来进行换行

        LF:\n 转义字符表示换行,Unix/Linux/Mac OS X操作系统采用单个字符LF来进行换行

        CRLF:\r\n 表示回车并换行,Windows操作系统采用两个字符来进行换行,即CRLF

enum line_ending_type
{CR,  // i.e. "\r"LF,  // i.e. "\n"CRLF // i.e. "\r\n"
};// ----------------------------------------------------------------------------------------base64::line_ending_type base64::line_ending () const{return eol_style;}// ----------------------------------------------------------------------------------------void base64::set_line_ending (line_ending_type eol_style_){eol_style = eol_style_;}// ----------------------------------------------------------------------------------------

        根据RFC822规定,BASE64Encoder编码每76个字符,还需要加上一个回车换行,所以dlib这里定义了换行符。

三、构造函数

        这里组织了编码、解码的字符数组,下面是关于初始值的说明。

/*!INITIAL VALUE- bad_value == 100- encode_table == a pointer to an array of 64 chars- where x is a 6 bit value the following is true:- encode_table[x] == the base64 encoding of x- decode_table == a pointer to an array of UCHAR_MAX chars- where x is any char value:- if (x is a valid character in the base64 coding scheme) then- decode_table[x] == the 6 bit value that x encodes- else- decode_table[x] == bad_value CONVENTION- The state of this object never changes so just refer to itsinitial value.!*/

        源码代码

    base64::base64 () : encode_table(0),decode_table(0),bad_value(100),eol_style(LF){try{encode_table = new char[64];decode_table = new unsigned char[UCHAR_MAX];}catch (...){if (encode_table) delete [] encode_table;if (decode_table) delete [] decode_table;throw;}// now set up the tables with the right stuffencode_table[0] = 'A';encode_table[17] = 'R';encode_table[34] = 'i';encode_table[51] = 'z';encode_table[1] = 'B';encode_table[18] = 'S';encode_table[35] = 'j';encode_table[52] = '0';encode_table[2] = 'C';encode_table[19] = 'T';encode_table[36] = 'k';encode_table[53] = '1';encode_table[3] = 'D';encode_table[20] = 'U';encode_table[37] = 'l';encode_table[54] = '2';encode_table[4] = 'E';encode_table[21] = 'V';encode_table[38] = 'm';encode_table[55] = '3';encode_table[5] = 'F';encode_table[22] = 'W';encode_table[39] = 'n';encode_table[56] = '4';encode_table[6] = 'G';encode_table[23] = 'X';encode_table[40] = 'o';encode_table[57] = '5';encode_table[7] = 'H';encode_table[24] = 'Y';encode_table[41] = 'p';encode_table[58] = '6';encode_table[8] = 'I';encode_table[25] = 'Z';encode_table[42] = 'q';encode_table[59] = '7';encode_table[9] = 'J';encode_table[26] = 'a';encode_table[43] = 'r';encode_table[60] = '8';encode_table[10] = 'K';encode_table[27] = 'b';encode_table[44] = 's';encode_table[61] = '9';encode_table[11] = 'L';encode_table[28] = 'c';encode_table[45] = 't';encode_table[62] = '+';encode_table[12] = 'M';encode_table[29] = 'd';encode_table[46] = 'u';encode_table[63] = '/';encode_table[13] = 'N';encode_table[30] = 'e';encode_table[47] = 'v';encode_table[14] = 'O';encode_table[31] = 'f';encode_table[48] = 'w';encode_table[15] = 'P';encode_table[32] = 'g';encode_table[49] = 'x';encode_table[16] = 'Q';encode_table[33] = 'h';encode_table[50] = 'y';// we can now fill out the decode_table by using the encode_tablefor (int i = 0; i < UCHAR_MAX; ++i){decode_table[i] = bad_value;}for (unsigned char i = 0; i < 64; ++i){decode_table[(unsigned char)encode_table[i]] = i;}}

四、析构函数

        这里删除了构造函数里面的数组。

    base64::~base64 (){delete [] encode_table;delete [] decode_table;}

五、编码函数

        1、这里首先了解streambuf::sgetn,是用于从streambuf对象的缓冲区中获取字符序列,获取 get 指针后面的nCount个字符并将它们存储在从pch开始的区域中。当streambuf对象中剩余的字符少于nCount时, sgetn获取剩余的任何字符。该函数重新定位 get 指针以跟随获取的字符。

        2、每76个字符,还需要加上一个回车换行,所以看到counter = 19,这里的76个字符是指写入到输出流的。

    void base64::encode (std::istream& in_,std::ostream& out_) const{using namespace std;streambuf& in = *in_.rdbuf();streambuf& out = *out_.rdbuf();unsigned char inbuf[3];unsigned char outbuf[4];streamsize status = in.sgetn(reinterpret_cast<char*>(&inbuf),3);unsigned char c1, c2, c3, c4, c5, c6;int counter = 19;// while we haven't hit the end of the input streamwhile (status != 0){if (counter == 0){counter = 19;// write a newlinechar ch;switch (eol_style){case CR:ch = '\r';if (out.sputn(&ch,1)!=1)throw std::ios_base::failure("error occurred in the base64 object");break;case LF:ch = '\n';if (out.sputn(&ch,1)!=1)throw std::ios_base::failure("error occurred in the base64 object");break;case CRLF:ch = '\r';if (out.sputn(&ch,1)!=1)throw std::ios_base::failure("error occurred in the base64 object");ch = '\n';if (out.sputn(&ch,1)!=1)throw std::ios_base::failure("error occurred in the base64 object");break;default:DLIB_CASSERT(false,"this should never happen");}}--counter;if (status == 3){// encode the bytes in inbuf to base64 and write them to the output streamc1 = inbuf[0]&0xfc;c2 = inbuf[0]&0x03;c3 = inbuf[1]&0xf0;c4 = inbuf[1]&0x0f;c5 = inbuf[2]&0xc0;c6 = inbuf[2]&0x3f;outbuf[0] = c1>>2;outbuf[1] = (c2<<4)|(c3>>4);outbuf[2] = (c4<<2)|(c5>>6);outbuf[3] = c6;outbuf[0] = encode_table[outbuf[0]];outbuf[1] = encode_table[outbuf[1]];outbuf[2] = encode_table[outbuf[2]];outbuf[3] = encode_table[outbuf[3]];// write the encoded bytes to the output streamif (out.sputn(reinterpret_cast<char*>(&outbuf),4)!=4){throw std::ios_base::failure("error occurred in the base64 object");}// get 3 more input bytesstatus = in.sgetn(reinterpret_cast<char*>(&inbuf),3);continue;}else if (status == 2){// we are at the end of the input stream and need to add some padding// encode the bytes in inbuf to base64 and write them to the output streamc1 = inbuf[0]&0xfc;c2 = inbuf[0]&0x03;c3 = inbuf[1]&0xf0;c4 = inbuf[1]&0x0f;c5 = 0;outbuf[0] = c1>>2;outbuf[1] = (c2<<4)|(c3>>4);outbuf[2] = (c4<<2)|(c5>>6);outbuf[3] = '=';outbuf[0] = encode_table[outbuf[0]];outbuf[1] = encode_table[outbuf[1]];outbuf[2] = encode_table[outbuf[2]];// write the encoded bytes to the output streamif (out.sputn(reinterpret_cast<char*>(&outbuf),4)!=4){throw std::ios_base::failure("error occurred in the base64 object");}break;}else // in this case status must be 1 {// we are at the end of the input stream and need to add some padding// encode the bytes in inbuf to base64 and write them to the output streamc1 = inbuf[0]&0xfc;c2 = inbuf[0]&0x03;c3 = 0;outbuf[0] = c1>>2;outbuf[1] = (c2<<4)|(c3>>4);outbuf[2] = '=';outbuf[3] = '=';outbuf[0] = encode_table[outbuf[0]];outbuf[1] = encode_table[outbuf[1]];// write the encoded bytes to the output streamif (out.sputn(reinterpret_cast<char*>(&outbuf),4)!=4){throw std::ios_base::failure("error occurred in the base64 object");}break;}} // while (status != 0)// make sure the stream buffer flushes to its I/O channelout.pubsync();}

六、解码函数

    void base64::decode (std::istream& in_,std::ostream& out_) const{using namespace std;streambuf& in = *in_.rdbuf();streambuf& out = *out_.rdbuf();unsigned char inbuf[4];unsigned char outbuf[3];int inbuf_pos = 0;streamsize status = in.sgetn(reinterpret_cast<char*>(inbuf),1);// only count this character if it isn't some kind of fillerif (status == 1 && decode_table[inbuf[0]] != bad_value )++inbuf_pos;unsigned char c1, c2, c3, c4, c5, c6;streamsize outsize;// while we haven't hit the end of the input streamwhile (status != 0){// if we have 4 valid charactersif (inbuf_pos == 4){inbuf_pos = 0;// this might be the end of the encoded data so we need to figure out if // there was any padding applied.outsize = 3;if (inbuf[3] == '='){if (inbuf[2] == '=')outsize = 1;elseoutsize = 2;}// decode the incoming charactersinbuf[0] = decode_table[inbuf[0]];inbuf[1] = decode_table[inbuf[1]];inbuf[2] = decode_table[inbuf[2]];inbuf[3] = decode_table[inbuf[3]];// now pack these guys into bytes rather than 6 bit chunksc1 = inbuf[0]<<2;c2 = inbuf[1]>>4;c3 = inbuf[1]<<4;c4 = inbuf[2]>>2;c5 = inbuf[2]<<6;c6 = inbuf[3];outbuf[0] = c1|c2;outbuf[1] = c3|c4;outbuf[2] = c5|c6;// write the encoded bytes to the output streamif (out.sputn(reinterpret_cast<char*>(&outbuf),outsize)!=outsize){throw std::ios_base::failure("error occurred in the base64 object");}}// get more input characters status = in.sgetn(reinterpret_cast<char*>(inbuf + inbuf_pos),1);// only count this character if it isn't some kind of filler if ((decode_table[inbuf[inbuf_pos]] != bad_value || inbuf[inbuf_pos] == '=') && status != 0)++inbuf_pos;} // while (status != 0)if (inbuf_pos != 0){ostringstream sout;sout << inbuf_pos << " extra characters were found at the end of the encoded data."<< "  This may indicate that the data stream has been truncated.";// this happens if we hit EOF in the middle of decoding a 24bit block.throw decode_error(sout.str());}// make sure the stream buffer flushes to its I/O channelout.pubsync();}

七、源码地址

dlib/dlib/base64 at master · davisking/dlib · GitHubA toolkit for making real world machine learning and data analysis applications in C++ - dlib/dlib/base64 at master · davisking/dlibhttps://github.com/davisking/dlib/tree/master/dlib/base64

这篇关于C/C++学习笔记 dlib中的base64编码源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/779849

相关文章

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

Python中的Walrus运算符分析示例详解

《Python中的Walrus运算符分析示例详解》Python中的Walrus运算符(:=)是Python3.8引入的一个新特性,允许在表达式中同时赋值和返回值,它的核心作用是减少重复计算,提升代码简... 目录1. 在循环中避免重复计算2. 在条件判断中同时赋值变量3. 在列表推导式或字典推导式中简化逻辑

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Java程序进程起来了但是不打印日志的原因分析

《Java程序进程起来了但是不打印日志的原因分析》:本文主要介绍Java程序进程起来了但是不打印日志的原因分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java程序进程起来了但是不打印日志的原因1、日志配置问题2、日志文件权限问题3、日志文件路径问题4、程序

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a