punycode編碼

枚舉變量

base = 36  tmin = 1 tmax = 26(閾值) skew = 38 damp = 70 initial_bias = 72 initial_n =128(0x80 ) delimiter = 0x2d(分隔符 -)

 

basic (cp)  判斷是不是基本字符,即ASCII以內字符

 

delim(cp)  判斷是不是 -”符號

 

decode_digitcp)將punycode 36進制數轉換成整數

 

encode_digit (d,flag) 將字符轉換成 0..25代表字面,26..35表示數字

 

maxint = -1

 

adapt (delta,numpoints,firsttime)   delta 是增量,numpoints是當前所有已編碼的碼位數量

 

可變長整數:

傳統的可變長整數,如,100,2983等等等,但是,這些可變長整數一旦連接在一起,1002983,則不能知道兩個整數的界限了,於是,要構造一個新的可變長整數格式:

每一位有一個閾值,t(j)

恰好只有一個權重最大的digit_j<t(j),從而可以區分各個數。

如,734251…..    對應的閾值爲,2,3,5,5,5,5….

因爲7>2,3=3,2<5,所以,2就是那個權重最大的digit,因此,734是一個數,類推,只,251是一個數。

 

這裏,可以將734,251理解成punycode編碼後的值,只需要將其解碼:各位數,乘以各自權重求和,就可以得到對應的Unicode,從而得到對應的漢子。

 

權重公式如下:

 

W(0)  =  1

W (j)  = w( j-1 ) * (base  -  t(j-1) ) j>0

 

閾值公式如下:

 

t(j)  =  base * ( j+1 ) – bias

 

如果計算出t(j) 小於tmin,則t(j) = tmin, 大於tmax t(j) = tmax

 

Bias 的值是根據貝葉斯調製而得:

 

1.  爲了避免下步操作數據溢出,堆會按照比例縮小

第一次縮小,delta= delta/damp

第二次及以後,delta= delta/2

 

2.  堆的增加補償了下個堆會被放入更長的字符串裏:

Delta = delta + delta/numpoints

3.堆不斷減小,直到落入界限值

for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base)

     {

           delta /= base - tmin;

     }

4.得到bias 的值

k + (base - tmin + 1) * delta / (delta + skew);

 

相反的過程,就是編碼過程,編碼過程,就是將Unicode差值,編碼成可變長整數的過程,而這個可變長整數的取值範圍是a~z,0~936進制。

 

編碼步驟:

1.       基礎碼分離:先掃描全部輸入,將ASCII部分,按照原來的次序排在前面的部分,插入‘-’用以分離

2.       把字按Unicode大小遞增排序

3.       計算相鄰Unicode碼差值

4.       差值乘以初始碼位置序號+排序後序號,得到增量

5.       對得到增量編碼成base-36編碼

6.       處理所有增量

 

 

如“中國互聯網網絡中心!”

 

變量初始值

n=initial_n = 128

delta = 0

bias = initial_bias = 72

h = out(out爲輸出字符串當前輸出的位置,由於字符串中有!,所以,目前的h=out= 1)

 

對應的Unicode

20013  22269  20114 32852 32593 32593 32476 20013 24515 33

 

除去對‘!’處理,現在看對中文編碼部分

 

 

參數m記錄當前Unicode最小值,n代表上一個最小的Unicode值,因爲‘ 國’的Unicode編碼值大於‘中’字,所以,先對‘中’進行處理 (下面過程不考慮報錯情況)

 

所以,當前

m= 20013   n = 128

對應的delta = m-n*(h+1) = 39770

這個過程之後,n=m,爲下一次計算差值做好準備。

Punycode所要編碼的項目,則就是這個delta值,即,後一個字符與前一個字符的Unicode數值之差。

 

接下來是編碼過程,編碼過程是將39770由地位向高位一位一位的編碼成一個可變長整數的過程。

 

先以十進制爲例,如816這個數,要求出各位數值,則得從低位開始計算,816%10 = 6 則得出,低位的第一位是6,然後,816/10 =81, 81%10=1,則其次低位爲1,再之,81/10=8, 8%10 = 8 ,則最高位爲8

 

下面的代碼則是針對base_36 做相同的操作

 

for (q = delta, k = base;; k += base)

                     {

                         if (out >= max_out)

                            return punycode_big_output;

              t = k <= bias /* + tmin */ ? tmin :         /* +tmin not needed */

                         k >= bias + tmax ? tmax : k - bias;

                         if (q < t)

                            break;

              output[out++] = encode_digit (t + (q - t) % (base - t), 0);

                         q = (q - t) / (base - t);

                   }

 

               output[out++] = encode_digit (q, case_flags && case_flags[j]);

                     bias = adapt (delta, h + 1, h == b);

 

首先計算低位的閾值,閾值的公式是

t = base*(j+1)-bias

而如果t大於tmax,則t=tmax,反之,t= tmin

j表示當前的位數,從0增長

bias,初始值爲initial_bias = 72

 

第一次,t + (q - t) % (base - t) 可以得出 最低位 10 經過編碼成base_36 對應字符爲 k

只要q>t ,則表明,仍然是 39770這個數在編碼

繼而得到,其他位爲 16 32  0

對應編碼爲  q  6   ,a

所以,第一個字的編碼 kq6a

 

以此類圖,可以得到全部的編碼信息。

 

可以驗證,對於 39770這個數

 

權值

因爲

W(0)  = 1

W(j)  = wj-1*(base – t (j-1) )

 

w0 =1

w 1= w (0) * (36 - 1) = 35

w  (2)  = w(1)*36-1 =352

w (3)  = w (2) * (36 - 1)  = 353

 

而,39770 = 0 * 353 + 32*352 + 16*35 +10 * 1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章