base64等编解码-实例

本文是实现常用编码的实例,编码规则不讲解。

本文涉及编码种类

  • base58
  • base64
  • protobuf
  • json
  • xml

base58

源于bitcoin里面的设计,为了支付方便,相比Base64,Base58不使用数字"0",字母大写"O",字母大写"I",和字母小写"l",以及"+“和”/"符号。

base64 版本1

base64有很多不同的实现方法,在阅读ceph的代码时,看到的是我觉得最精简,代码风格完美的代码,因此本文引用来自ceph源码里面的编码。


#include <linux/errno.h>

/*
 * base64 encode/decode.
 */

const char *pem_key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static int encode_bits(int c)
{
	return pem_key[c];
}

static int decode_bits(char c)
{
	if (c >= 'A' && c <= 'Z')
		return c - 'A';
	if (c >= 'a' && c <= 'z')
		return c - 'a' + 26;
	if (c >= '0' && c <= '9')
		return c - '0' + 52;
	if (c == '+')
		return 62;
	if (c == '/')
		return 63;
	if (c == '=')
		return 0; /* just non-negative, please */
	return -EINVAL;	
}

/*
函数功能:实现base64编码
*/
int ceph_armor(char *dst, const char *src, const char *end)
{
	int olen = 0;
	int line = 0;

	while (src < end) {
		unsigned char a, b, c;

		a = *src++;
		*dst++ = encode_bits(a >> 2);
		if (src < end) {
			b = *src++;
			*dst++ = encode_bits(((a & 3) << 4) | (b >> 4));
			if (src < end) {
				c = *src++;
				*dst++ = encode_bits(((b & 15) << 2) | (c >> 6));
				*dst++ = encode_bits(c & 63);
			} else {
				*dst++ = encode_bits((b & 15) << 2);
				*dst++ = '=';				
			}
		} else {
			*dst++ = encode_bits(((a & 3) << 4));
			*dst++ = '=';
			*dst++ = '=';
		}
		olen += 4;
		line += 4;
		if (line == 64) {
			line = 0;
			*(dst++) = '\n';
			olen++;
		}
	}
	return olen;
}

/*
函数功能:实现base64解码
*/
int ceph_unarmor(char *dst, const char *src, const char *end)
{
	int olen = 0;

	while (src < end) {
		int a, b, c, d;

		if (src < end && src[0] == '\n')
			src++;
		if (src + 4 > end)
			return -EINVAL;
		a = decode_bits(src[0]);
		b = decode_bits(src[1]);
		c = decode_bits(src[2]);
		d = decode_bits(src[3]);
		if (a < 0 || b < 0 || c < 0 || d < 0)
			return -EINVAL;

		*dst++ = (a << 2) | (b >> 4);
		if (src[2] == '=')
			return olen + 1;
		*dst++ = ((b & 15) << 4) | (c >> 2);
		if (src[3] == '=')
			return olen + 2;
		*dst++ = ((c & 3) << 6) | d;
		olen += 3;
		src += 4;
	}
	return olen;
}

base64 版本2

使用了openssl库来实现,封装了openssl的操作,形成一个壳子api

#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>

#include <string.h>


int encode_base64(const char * in, int in_len, char * out, int out_len)
{
	BIO *			bmem, *b64;
	BUF_MEM *		bptr;

	b64 				= BIO_new(BIO_f_base64());
	bmem				= BIO_new(BIO_s_mem());
	b64 				= BIO_push(b64, bmem);
	BIO_write(b64, in, in_len);

	if (BIO_flush(b64) < 0) {
		return - 1;
	}

	BIO_get_mem_ptr(b64, &bptr);

	int 			len = BIO_pending(bmem);

	if (out_len <= len) {
		return - 1;
	}

	memcpy(out, bptr->data, len);
	out[len - 1]		= '\0';

	BIO_free_all(b64);

	return 0;
}


int decode_base64(const char * in, int in_len, char * out, int out_len)
{
	BIO *			b64, *bmem;
	int 			ret;
	char			in_eol[in_len + 2];

	memcpy(in_eol, in, in_len);
	in_eol[in_len]		= '\n';
	in_eol[in_len + 1]	= '\0';

	b64 				= BIO_new(BIO_f_base64());
	bmem				= BIO_new_mem_buf((unsigned char *) in_eol, in_len + 1);
	bmem				= BIO_push(b64, bmem);

	ret 				= BIO_read(bmem, out, out_len);

	BIO_free_all(bmem);

	return ret;
}

结语:
由于c++工程师的缘故,我们写任何代码总喜欢流出来各种接口,参数可以是string 、vector、char *、list等等,一个简单的功能硬是衍生出来N个重载函数,也许这样写代码执行效率高一点,别人调用起来好用一些,搞得代码很臃肿。我在写本文时突然意识到,代码要精简,模在应用层也要有很好的分层,能牺牲效率,换来代码的精简是值得的。

因此我建议,c++开发人员在写基础函数时,只写一个或最多两个类型的参数接口就行了。

作为应用开发人员,而不是boost、stl库 或公司基础库开发人员,就是要勇于牺牲部分代码执行效率,换来代码的可维护性,这样你的代码才能长大,而且是健康的长大。

未完待续

发布了61 篇原创文章 · 获赞 63 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/jacky128256/article/details/103043497