//
// ConvertUtil.h
// MinaCppClient
//
// Created by yang3wei on 7/22/13.
// Copyright (c) 2013 yang3wei. All rights reserved.
//
#ifndef __MinaCppClient__ConvertUtil__
#define __MinaCppClient__ConvertUtil__
#include <string>
/**
* htonl 表示 host to network long ,用于将主机 unsigned int 型数据转换成网络字节顺序;
* htons 表示 host to network short ,用于将主机 unsigned short 型数据转换成网络字节顺序;
* ntohl、ntohs 的功能分别与 htonl、htons 相反。
*/
/**
* byte 不是一种新类型,在 C++ 中 byte 被定义的是 unsigned char 类型;
* 但在 C# 里面 byte 被定义的是 unsigned int 类型
*/
#ifndef TYPE_BYTE
#define TYPE_BYTE
typedef unsigned char byte;
#endif
#ifndef TYPE_UINT
#define TYPE_UINT
typedef unsigned int uint;
#endif
using namespace std;
#pragma mark int2bytes() & bytes2int()
/**
* 功能:uint 转 byte
* 心得:方法无返回的优点:做内存管理清爽整洁。
* 1.如果返回值为 int,float,long,double 等简单类型,直接返回即可;
* 2.如果返回值为分配在栈上的数组或对象,亦可直接返回;
* 3.如果返回值是分配在堆内存上的数组或对象,最好不要放在返回值里返回,做成出参的效果会比较好。
* 在 OpenGL 中此种设计模式比较常见
* 业界规范:出参在前,入参在后
*/
void uint2bytes(byte* out_pArrBytes, uint in_uiValue);
/**
* 功能:byte 转 uint
*/
uint bytes2uint(byte* in_pArrBytes);
#pragma mark hexStr2bytes() & bytes2hexStr()
/**
* 功能:char 转 int
* 返回:转换出来的 int
*/
// int hexChar2int(char c);
/**
* 功能:十六进制字符串转字节数组
* 返回:转换出来的字节数组的长度
* 注意:
*/
//int hexStr2bytes(std::string in_oStrHex, byte* out_pArrBytes);
void hexStr2bytes(byte* out_pArrBytes, int in_iArrSize, const char* in_pArrCharHex);
/**
* 功能:字节数组转十六进制字符串
* 返回:转换出来的十六进制字符串
*/
//std::string bytes2hexStr(byte* in_pArrBytes, int in_iSize);
void bytes2hexStr(string& out_oStrHex, byte* in_pArrBytes, int in_iArrSize);
#endif /* defined(__MinaCppClient__ConvertUtil__) */
//
// ConvertUtil.cpp
// MinaCppClient
//
// Created by yang3wei on 7/22/13.
// Copyright (c) 2013 yang3wei. All rights reserved.
//
#include "ConvertUtil.h"
#define HEX_ELEMENTS "0123456789ABCDEF"
/**
* 1.传参规范:
* 一般是输出 dest 在前面,输入 source 在后面。
*(库函数,一般都是输出在前面,输入在后面。例如字符串拷贝函数,strcpy)
* 2.此方法无必要做清零处理
* 譬如说 1111 0000 ,现在赋了一个值 0000 0001,不清零的话不会出现 1111 0001 这种情况
* 3.检查指针是否为 NULL,是个好习惯
*/
void uint2bytes(byte* out_pArrBytes, uint in_uiValue)
{
if (!out_pArrBytes)
{
printf("空指针!\n");
return;
}
out_pArrBytes[0] = (byte)(in_uiValue & 0xff);
out_pArrBytes[1] = (byte)((in_uiValue >> 8) & 0xff);
out_pArrBytes[2] = (byte)((in_uiValue >> 16) & 0xff);
out_pArrBytes[3] = (byte)((in_uiValue >> 24) & 0xff);
}
uint bytes2uint(byte* in_pArrBytes)
{
if (!in_pArrBytes)
{
printf("空指针!\n");
return 0;
}
uint t_uiRetVal = (uint)in_pArrBytes[0] & 0xff;
t_uiRetVal |= (((uint)in_pArrBytes[1] << 8) & 0xff00);
t_uiRetVal |= (((uint)in_pArrBytes[2] << 16) & 0xff0000);
t_uiRetVal |= (((uint)in_pArrBytes[3] << 24) & 0xff000000);
return t_uiRetVal;
}
/**
* 这个函数最大的问题,就是用了很多 if 判断语句,
* 运气不好的时候(执行到最后一行 return 0; 的时候),要比较 6 次,跳转 3次
* (每次 if 里的条件不成立,程序就要跳转的。当然是要去看汇编,里面才有跳转指令)
* switch 就更慢了,每个 case 都要比较一次。
* 其实有更简单、更快的算法 —— 用数组!
* 这叫 “查表法”,缺点就是空间消耗比较大。
* 定义一个有 256 个元素的数组,都不用写这个函数了,直接调用数组即可,函数里直接返回,还有调用函数的开销呢。
*/
int hexChar2int(char in_oChar)
{
if (in_oChar >= '0' && in_oChar <= '9')
{
return (in_oChar - '0');
}
if (in_oChar >= 'A' && in_oChar <= 'F')
{
return (in_oChar - 'A' + 10);
}
if (in_oChar >= 'a' && in_oChar <= 'f')
{
return (in_oChar - 'a' + 10);
}
return 0;
}
/**
* “耗费空间比较大” 的话,我感觉 256 不是很大啊,毕竟是 byte 而已
* 要想运算快的话,就要用 int 数组!
* 这里有个字节对齐的问题,字节对齐的话,访问速度快,字节没对齐,指针要调整 2 次。
* 这个是操作系统的原理,“字节对齐” 这个概念对各种操作系统都适用。
*/
const int g_oArrIntTable[256] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12,
13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
};
/**
* 用查表法,<< 4 也可以省掉,不过要用 2 个数组了
* in_oStrHex.at(i) 有一定的开销,建议不要用 stirng 传输入进来,传个指针(const char*)就够了
* 对大于 0 的整数,不要 /2 ,要 >>1
* 一个函数调用的开销有多大?如果不是 inline 的函数,开销和函数体内的运算比,就比较大了
*/
int hexStr2bytes(std::string in_oStrHex, byte* out_pArrBytes) {
int t_iSize = (int)in_oStrHex.length();
out_pArrBytes = new byte[t_iSize / 2];
for (int i = 0; i < t_iSize; i += 2)
{
out_pArrBytes[i / 2] = (hexChar2int(in_oStrHex.at(i)) << 4) | hexChar2int(in_oStrHex.at(i + 1));
}
return t_iSize / 2;
}
/**
* 上述函数改进后的写法
* 这里直接传 out_pArrBytes 指针进来,内存分配在调用该方法之前就弄好(因为长度是已知的)
* in_iArrSize 是 out_pArrBytes 数组的长度;
* (in_iArrSize * 2) 是十六进制字符串的长度。
* 使用 new 的时候一定要防止失败时出现异常
* 用 new 的话,一定要放在 try{} catch{} 里,内存不够或者内存碎片过多没有连续内存分配的时候就会出错,
* 软件写得不好,长时间运行就有可能出现这种情况。出现异常,没有处理代码的话,软件马上就崩溃了
* 我们用 new<nothrow> , 分配后检查指针是否为 NULL。new 有 4 种用法,网上有相关资料。
*/
void hexStr2bytes(byte* out_pArrBytes, int in_iArrSize, const char* in_pArrCharHex)
{
if (!out_pArrBytes)
{
printf("空指针!\n");
return;
}
for (int i = 0; i < in_iArrSize << 1; i += 2)
{
char tmp_c0 = *(in_pArrCharHex + i);
char tmp_c1 = *(in_pArrCharHex + i + 1);
out_pArrBytes[i >> 1] = (g_oArrIntTable[tmp_c0] << 4) | g_oArrIntTable[tmp_c1];
}
}
/**
* 这个函数返回一个栈对象,没啥问题,就是效率低了点
* t_oStrHex在函数结束的时候要析构销毁,重新产生一个临时对象返回
* 所以这个函数最好增加一个输出的形参,可以用指针。
* string t_oStrHex; 要执行一个构造函数
* return t_oStrHex; 要执行一次析构函数,一次构造函数产生临时对象,赋值后临时对象再执行一次析构函数。
* strHexElements 这个 string 也是没必要使用的,这些底层函数,可以全部用指针操作的,没必要用 string(注意指针别越界)
*/
string bytes2hexStr(byte* in_pArrBytes, int in_iSize)
{
string t_oStrHex;
string strHexElements(HEX_ELEMENTS);
for (int i = 0; i < in_iSize; i ++)
{
t_oStrHex.append(1, strHexElements.at(0x0f & (in_pArrBytes[i] >> 4)));
t_oStrHex.append(1, strHexElements.at(0x0f & in_pArrBytes[i]));
}
return t_oStrHex;
}
/**
* 改良后的函数
*/
const int g_oArrIntHex[16] =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
void bytes2hexStr(string& out_oStrHex, byte* in_pArrBytes, int in_iArrSize)
{
if (!in_pArrBytes)
{
printf("空指针!\n");
return;
}
for (int i = 0; i < in_iArrSize; i ++)
{
out_oStrHex.append(1, g_oArrIntHex[0x0f & (in_pArrBytes[i] >> 4)]);
out_oStrHex.append(1, g_oArrIntHex[0x0f & in_pArrBytes[i]]);
}
}
//
// main.cpp
// MinaCppClient
//
// Created by yang3wei on 7/27/13.
//
//
#include "ConvertUtil.h"
/**
* 输出:
* a7a8a9aa -- a7a8a9aa
*/
int main(int argc, const char * argv[]) {
string t_oStrHex("a7a8a9aa");
int t_iArrSize = (int)t_oStrHex.length() / 2;
byte* t_pArrBytes = new byte[t_iArrSize];
hexStr2bytes(t_pArrBytes, t_iArrSize, t_oStrHex.c_str());
string t_oStrHex1;
bytes2hexStr(t_oStrHex1, t_pArrBytes, t_iArrSize);
printf("%s -- %s\n", t_oStrHex.c_str(), t_oStrHex1.c_str());
return 0;
}