Serialization：如何封裝資料

1. 1.
將數字轉換為文字，使用如 sprintf() 的函式，接著傳送文字。接收者會使用如 strtol() 函式解析文字，並轉換為數字。
2. 2.
直接以原始資料傳送，將指向資料的指標傳遞給 send()。
3. 3.
將數字編碼（encode）為可移植的二進制格式，接收者會將它解碼（decode）。

［序幕］ Beej 說：＂我偏好上面的第三個方法！＂ ［結束］
（在我開始熱血介紹本章節之前，我應該要跟你說有現成的函式庫可以做這件事情，而要自製個可移植及無錯誤的作品會是相當大的挑戰。所以在決定要自己實作這部分時，可以先四處看看，並做完你的家庭作業。我在這裡引用些類似這個作品的有趣的資訊。）

double d = 3490.15926535;
send(s, &d, sizeof d, 0); /* 危險，不具可移植性！ */

double d;
recv(s, &d, sizeof d, 0); /* 危險，不具可移植性！ */

#include <stdint.h>
uint32_t htonf(float f)
{
uint32_t p;
uint32_t sign;
if (f < 0) { sign = 1; f = -f; }
else { sign = 0; }
p = ((((uint32_t)f)&0x7fff)<<16) | (sign<<31); // whole part and sign
p |= (uint32_t)(((f - (int)f) * 65536.0f))&0xffff; // fraction
return p;
}
float ntohf(uint32_t p)
{
float f = ((p>>16)&0x7fff); // whole part
f += (p&0xffff) / 65536.0f; // fraction
if (((p>>31)&0x1) == 0x1) { f = -f; } // sign bit set
return f;
}

#include <stdio.h>
int main(void)
{
float f = 3.1415926, f2;
uint32_t netf;
netf = htonf(f); // 轉換為 "network" 形式
f2 = ntohf(netf); // 轉回測試
printf("Original: %f\n", f); // 3.141593
printf(" Network: 0x%08X\n", netf); // 0x0003243F
printf("Unpacked: %f\n", f2); // 3.141586
return 0;
}

#define pack754_32(f) (pack754((f), 32, 8))
#define pack754_64(f) (pack754((f), 64, 11))
#define unpack754_32(i) (unpack754((i), 32, 8))
#define unpack754_64(i) (unpack754((i), 64, 11))
uint64_t pack754(long double f, unsigned bits, unsigned expbits)
{
long double fnorm;
int shift;
long long sign, exp, significand;
unsigned significandbits = bits - expbits - 1; // -1 for sign bit
if (f == 0.0) return 0; // get this special case out of the way
// 檢查正負號並開始正規化
if (f < 0) { sign = 1; fnorm = -f; }
else { sign = 0; fnorm = f; }
// 取得 f 的正規化型式並追蹤指數
shift = 0;
while(fnorm >= 2.0) { fnorm /= 2.0; shift++; }
while(fnorm < 1.0) { fnorm *= 2.0; shift--; }
fnorm = fnorm - 1.0;
// 計算有效位數資料的二進位格式（非浮點數）
significand = fnorm * ((1LL<<significandbits) + 0.5f);
// get the biased exponent
exp = shift + ((1<<(expbits-1)) - 1); // shift + bias
// 傳回最後的解答
return (sign<<(bits-1)) | (exp<<(bits-expbits-1)) | significand;
}
long double unpack754(uint64_t i, unsigned bits, unsigned expbits)
{
long double result;
long long shift;
unsigned bias;
unsigned significandbits = bits - expbits - 1; // -1 for sign bit
if (i == 0) return 0.0;
// pull the significand
result = (i&((1LL<<significandbits)-1)); // mask
result /= (1LL<<significandbits); // convert back to float
result += 1.0f; // add the one back on
// deal with the exponent
bias = (1<<(expbits-1)) - 1;
shift = ((i>>significandbits)&((1LL<<expbits)-1)) - bias;
while(shift > 0) { result *= 2.0; shift--; }
while(shift < 0) { result /= 2.0; shift++; }
// sign it
result *= (i>>(bits-1))&1? -1.0: 1.0;
return result;
}

#include <stdio.h>
#include <stdint.h> // 定義 uintN_t 型別
#include <inttypes.h> // 定義 PRIx macros
int main(void)
{
float f = 3.1415926, f2;
double d = 3.14159265358979323, d2;
uint32_t fi;
uint64_t di;
fi = pack754_32(f);
f2 = unpack754_32(fi);
di = pack754_64(d);
d2 = unpack754_64(di);
printf("float before : %.7f\n", f);
printf("float encoded: 0x%08" PRIx32 "\n", fi);
printf("float after : %.7f\n\n", f2);
printf("double before : %.20lf\n", d);
printf("double encoded: 0x%016" PRIx64 "\n", di);
printf("double after : %.20lf\n", d2);
return 0;
}

float before : 3.1415925
float encoded: 0x40490FDA
float after : 3.1415925
double before : 3.14159265358979311600
double encoded: 0x400921FB54442D18
double after : 3.14159265358979311600

（The Practice of Programming 是值得閱讀的好書，Zeus saves a kitten every time I recommend it。）

「這段程式碼參考到上面的 pack754() 函式，packi*() 函式的運作方式類似 htons() 家族，除非它們是封裝到一個 char 陣列（array）而不是另一個整數。」
#include <ctype.h>
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
// 供浮點數型別的變動位元
// 隨著架構而變動
typedef float float32_t;
typedef double float64_t;
/*
** packi16() -- store a 16-bit int into a char buffer (like htons())
*/
void packi16(unsigned char *buf, unsigned int i)
{
*buf++ = i>>8; *buf++ = i;
}
/*
** packi32() -- store a 32-bit int into a char buffer (like htonl())
*/
void packi32(unsigned char *buf, unsigned long i)
{
*buf++ = i>>24; *buf++ = i>>16;
*buf++ = i>>8; *buf++ = i;
}
/*
** unpacki16() -- unpack a 16-bit int from a char buffer (like ntohs())
*/
unsigned int unpacki16(unsigned char *buf)
{
return (buf<<8) | buf;
}
/*
** unpacki32() -- unpack a 32-bit int from a char buffer (like ntohl())
*/
unsigned long unpacki32(unsigned char *buf)
{
return (buf<<24) | (buf<<16) | (buf<<8) | buf;
}
/*
** pack() -- store data dictated by the format string in the buffer
**
** h - 16-bit l - 32-bit
** c - 8-bit char f - float, 32-bit
** s - string (16-bit length is automatically prepended)
*/
int32_t pack(unsigned char *buf, char *format, ...)
{
va_list ap;
int16_t h;
int32_t l;
int8_t c;
float32_t f;
char *s;
int32_t size = 0, len;
va_start(ap, format);
for(; *format != '\0'; format++) {
switch(*format) {
case 'h': // 16-bit
size += 2;
h = (int16_t)va_arg(ap, int); // promoted
packi16(buf, h);
buf += 2;
break;
case 'l': // 32-bit
size += 4;
l = va_arg(ap, int32_t);
packi32(buf, l);
buf += 4;
break;
case 'c': // 8-bit
size += 1;
c = (int8_t)va_arg(ap, int); // promoted
*buf++ = (c>>0)&0xff;
break;
case 'f': // float
size += 4;
f = (float32_t)va_arg(ap, double); // promoted
l = pack754_32(f); // convert to IEEE 754
packi32(buf, l);
buf += 4;
break;
case 's': // string
s = va_arg(ap, char*);
len = strlen(s);
size += len + 2;
packi16(buf, len);
buf += 2;
memcpy(buf, s, len);
buf += len;
break;
}
}
va_end(ap);
return size;
}
/*
** unpack() -- unpack data dictated by the format string into the buffer
*/
void unpack(unsigned char *buf, char *format, ...)
{
va_list ap;
int16_t *h;
int32_t *l;
int32_t pf;
int8_t *c;
float32_t *f;
char *s;
int32_t len, count, maxstrlen=0;
va_start(ap, format);
for(; *format != '\0'; format++) {
switch(*format) {
case 'h': // 16-bit
h = va_arg(ap, int16_t*);
*h = unpacki16(buf);
buf += 2;
break;
case 'l': // 32-bit
l = va_arg(ap, int32_t*);
*l = unpacki32(buf);
buf += 4;
break;
case 'c': // 8-bit
c = va_arg(ap, int8_t*);
*c = *buf++;
break;
case 'f': // float
f = va_arg(ap, float32_t*);
pf = unpacki32(buf);
buf += 4;
*f = unpack754_32(pf);
break;
case 's': // string
s = va_arg(ap, char*);
len = unpacki16(buf);
buf += 2;
if (maxstrlen > 0 && len > maxstrlen) count = maxstrlen - 1;
else count = len;
memcpy(s, buf, count);
s[count] = '\0';
buf += len;
break;
default:
if (isdigit(*format)) { // track max str len
maxstrlen = maxstrlen * 10 + (*format-'0');
}
}
if (!isdigit(*format)) maxstrlen = 0;
}
va_end(ap);
}