• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Que's C++ Studio

            大道至簡
            posts - 3, comments - 8, trackbacks - 0, articles - 0
            理解和使用zlib庫 - 我的個人救贖
            作者: 闕榮文
            日期: 2016.6.2
            0. 很多年以前我曾經(jīng)寫過一篇文章(http://blog.csdn.net/querw/article/details/1452041)簡單介紹 zlib 的使用方法,老實說當時自己都不是很明白 zlib 是怎么回事,現(xiàn)在想起來那個時候年輕嘛,膽子大,臉皮厚...有時候想到自己這樣一篇半吊子的東西在網(wǎng)絡(luò)上傳播,心不自安,希望用一篇新的文章救贖少不更事的無知.
            1. deflate算法, zlib 格式, gzip 格式
            本文并不是一篇介紹壓縮算法的文章,請讀者自行查閱關(guān)于 LZ77 算法的詳情.deflate 是 LZ77 算法的一個增強版,對各種數(shù)據(jù)提供無損壓縮,是 zlib 目前唯一實現(xiàn)的壓縮算法.
            一段數(shù)據(jù)經(jīng)過 deflate 算法壓縮之后形成一段輸出數(shù)據(jù),這段輸出數(shù)據(jù)就是純粹的壓縮數(shù)據(jù),沒有任何額外信息如長度,校驗和等.可以直接存儲或者在網(wǎng)絡(luò)中傳輸原始壓縮數(shù)據(jù)并由 inflate 算法解壓縮,但是用戶要保證數(shù)據(jù)的完整性.
            當然,我們也可以為這段原始數(shù)據(jù)額外添加一個 zlib 格式(rfc1950)的數(shù)據(jù)頭/尾,使用 adler32 校驗和,定義如下:
                 +---+---+
                 |CMF|FLG|    (more-->)
                 +---+---+
                 (if FLG.FDICT set)
                 +-----+-----+-----+-----+
                 |       DICTID          | (more-->)
                 +-----+-----+-----+-----+
                 +========================+----+-----+-----+----+
                 | ...compressed data...  |        ADLER32      |
                 +========================+----+-----+-----+----+
            2個字節(jié)的 zlib 頭, 4個字節(jié)的字典(可選), deflate 原始壓縮數(shù)據(jù), 4個字節(jié)的 adler32 校驗和. 這是一種非常簡潔的數(shù)據(jù)包裝格式.
            gzip(rfc1952) 是不同于 zlib 的另外一種格式的數(shù)據(jù)頭/尾,使用 CRC32 校驗和,定義如下:
                 +---+---+---+---+---+---+---+---+---+---+
                 |ID1|ID2|CM |FLG|     MTIME     |XFL|OS | (more-->)
                 +---+---+---+---+---+---+---+---+---+---+
                 (if FLG.FEXTRA set)
                 +---+---+=================================+
                 | XLEN  |...XLEN bytes of "extra field"...| (more-->)
                 +---+---+=================================+
                 (if FLG.FNAME set)
                 +=========================================+
                 |...original file name, zero-terminated...| (more-->)
                 +=========================================+
                 (if FLG.FCOMMENT set)
                 +===================================+
                 |...file comment, zero-terminated...| (more-->)
                 +===================================+
                 (if FLG.FHCRC set)
                 +---+---+
                 | CRC16 |
                 +---+---+
                 +=======================+
                 |...compressed blocks...| (more-->)
                 +=======================+
                 +---+---+---+---+---+---+---+---+
                 |     CRC32     |     ISIZE     |
                 +---+---+---+---+---+---+---+---+
            一個 gzip 段落由 10 字節(jié)長度的頭,若干可選的附加段(由 gzip header中的 flag 字段標識是否存在),壓縮數(shù)據(jù),和4字節(jié)的 CRC32 校驗和,4字節(jié)的原文長度組成.
            解壓器根據(jù)壓縮數(shù)據(jù)可以知道壓縮數(shù)據(jù)流什么時候結(jié)束,所以沒必要在數(shù)據(jù)頭中包含壓縮后的數(shù)據(jù)長度字段, ISIZE 等于原文長度 % 2^32 (對于小于 4GB 的數(shù)據(jù)來說 ISIZE 就是原文的長度).
            zip 也是一種包裝格式,應(yīng)該說是一組格式約定,主要針對多個文件提供打包功能,所以 zip 格式中有很多關(guān)于文件目錄的信息,具體格式可以在網(wǎng)上搜一搜,并參考 zlib 源碼包中的 minizip 項目.
            在 zlib 文檔中, "zlib" 這個詞有兩種意思(很奇怪的命名),一是表示 zlib 代碼庫本身, 二是表示對 deflate 原始壓縮數(shù)據(jù)的 "zlib" 包裝格式.為了便于區(qū)分,后面我用 "libzlib" 表示前者, 用 "zlib" 表示后者.
            2. libzlib 設(shè)計思路 - 流
            流的定義如下:
            typedef struct z_stream_s {
                z_const Bytef *next_in;     /* next input byte */
                uInt     avail_in;  /* number of bytes available at next_in */
                uLong    total_in;  /* total number of input bytes read so far */
                Bytef    *next_out; /* next output byte should be put there */
                uInt     avail_out; /* remaining free space at next_out */
                uLong    total_out; /* total number of bytes output so far */
                z_const char *msg;  /* last error message, NULL if no error */
                struct internal_state FAR *state; /* not visible by applications */
                alloc_func zalloc;  /* used to allocate the internal state */
                free_func  zfree;   /* used to free the internal state */
                voidpf     opaque;  /* private data object passed to zalloc and zfree */
                int     data_type;  /* best guess about the data type: binary or text */
                uLong   adler;      /* adler32 value of the uncompressed data */
                uLong   reserved;   /* reserved for future use */
            } z_stream;
            由輸入數(shù)據(jù) xxxx_in 和輸出數(shù)據(jù) xxxx_out 組成,原始數(shù)據(jù)從輸入端流入,變?yōu)閴嚎s數(shù)據(jù)從輸出端流出(解壓縮反過來).
            編程的時候,用戶不停的"喂"數(shù)據(jù)到 next_in 并指定它的長度 avail_in 調(diào)用壓縮函數(shù),然后從 next_out 得到壓縮后的數(shù)據(jù),長度是 avail_out.這就是整個 zlib 庫的接口的設(shè)計思路.
            msg: 錯誤信息
            zalloc / zfree / opaque: 類似于 C++ STL 中的 allocator 的作用,如果要定制內(nèi)存管理可以自己編寫內(nèi)存分配回收函數(shù).
            adler: adler32 / CRC32 校驗和.
            3. libzlib 接口
            根據(jù)前文對數(shù)據(jù)包裝格式的說明可以知道真正的壓縮數(shù)據(jù)其實都是相同的,由 deflate 算法計算得到,區(qū)別在于包裝格式的不同,所以 libzlib API 細節(jié)的微妙之處都在于如何配置壓縮器/解壓器以得到不同包裝格式的輸出數(shù)據(jù).
            3.1 基本 API
            ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)):
            初始化 z_stream, 如果要使用默認的內(nèi)存管理函數(shù)必須把 zalloc / zfree / opaque 設(shè)置為 Z_NULL.輸出帶有 zlib 數(shù)據(jù)頭/尾的壓縮流.
            用此函數(shù)初始化得到的壓縮器就默認輸出 zlib 格式的壓縮數(shù)據(jù).如果我們希望得到 gzip 格式或者原始的壓縮數(shù)據(jù)怎么做呢? 于是引出另一個提供更多選項的壓縮器初始化函數(shù):
            ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
                                                 int  level,
                                                 int  method,
                                                 int  windowBits,
                                                 int  memLevel,
                                                 int  strategy)):
            不同包裝格式的數(shù)據(jù)輸出由 windowBits 這個參數(shù)控制:
            8 ~ 15: 輸出 zlib 數(shù)據(jù)頭/尾, deflateInit() 中這個參數(shù)值固定為 15, 就是 zconf.h 中定義的 MAX_WBITS 的值.
            -8 ~ -15: 輸出原始的壓縮數(shù)據(jù)不含任何數(shù)據(jù)頭/尾. 如果沒有特殊要求,使用 -15 就可以,表示內(nèi)部使用 32K 的 LZ77 滑動窗口.
            24 ~ 31: 輸出 gzip 格式的數(shù)據(jù),默認提供一個所有設(shè)置都清零的 gzip 數(shù)據(jù)頭,如果要自定義這個數(shù)據(jù)頭,可以在初始化之后, deflate() 之前調(diào)用 deflateSetHeader().
            level: 壓縮級別 0 ~ 9. 0 表示不壓縮, 1 表示速度最快, 9 表示壓縮比最高. Z_DEFAULT_COMPRESSION (-1) 表示使用默認設(shè)置.
            method: Z_DEFLATED(8) 只是唯一支持的壓縮算法.
            memLevel: 控制 libzlib 內(nèi)部使用內(nèi)存的大小, 1 ~ 9 數(shù)字越小內(nèi)存用量也越小,花費時間越多.默認值是8.
            strategy: 內(nèi)部壓縮算法的編碼策略,如果沒有特殊要求,設(shè)置為 Z_DEFAULT_STRATEGY 就可以了(如果你有特殊要求,那你自然知道其余選項 Z_FILTERED / Z_HUFFMAN_ONLY / Z_RLE / Z_FIXED 是什么意思).
            ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)):
            flush: 如果沒有特殊需求,我們可以先以 flush = Z_NO_FLUSH 調(diào)用 deflate(),在輸入數(shù)據(jù)壓縮完成之后,還需要以 flush = Z_FINISH 調(diào)用并確認 deflate() 返回 Z_STREAM_END 表示所有數(shù)據(jù)都已經(jīng)寫入到輸出緩沖,一個流結(jié)束.如果一次性輸入所有原文,那么也可以直接以 flush = Z_FINISH 調(diào)用 deflate(),這正是 compress() 的做法.
            用戶通過設(shè)置 z_stream 中 next_in / avail_in 指定的輸入把數(shù)據(jù)壓縮并更新 next_out / avail_out.輸入輸出緩沖區(qū)都由用戶分配. 還是舉個例子說明: 輸入緩沖區(qū)為 byte inbuf[1024] 那么 next_in = inbuf, avail_in = 1024. 因為在壓縮完成之前,用戶不可能知道壓縮后的數(shù)據(jù)長度,所以無法準確分配(除非調(diào)用 deflateBound()計算)輸出緩沖區(qū).用戶可以分配一個任意長度(大于6)的輸出緩沖,比如 byte outbuf[128], 那么 next_out = outbuf, avail_out = 128. 接下來調(diào)用 deflate,然后檢查 avail_in 表示輸入緩沖內(nèi)尚未被處理的數(shù)據(jù)長度,換而言之 1024 - avail_in 就得到了本次被處理的數(shù)據(jù)的長度.avail_out 表示輸出緩沖區(qū)的剩余空間, 128 - avail_out 就是本次得到的壓縮數(shù)據(jù)的長度, 只要 avail_in != 0 就重新設(shè)置avail_out 繼續(xù)壓縮,一旦 avail_in == 0 表示數(shù)據(jù)都已經(jīng)提交完畢,然后以參數(shù) Z_FINISH 調(diào)用 deflate(strm, Z_FINISH) 指示壓縮器,數(shù)據(jù)已經(jīng)提交完畢,請輸出 zlib 或者 gzip 的數(shù)據(jù)尾, 如果 deflate 返回 Z_STREAM_END 就表示數(shù)據(jù)尾也已經(jīng)輸出了,工作完成.即使配置壓縮器為輸出原始壓縮數(shù)據(jù)而不使用包裝格式,我們也要按照這個流程調(diào)用 deflate 確保得到完整的輸出數(shù)據(jù).
            ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)):
            釋放 z_stream.
            ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, uLong sourceLen));
            計算壓縮后的數(shù)據(jù)的長度,如果需要一次性壓縮一段內(nèi)存緩沖,可以調(diào)用它來估算輸出緩沖的最大長度.
            ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, gz_headerp head));
            設(shè)置自定義 gzip 頭,應(yīng)該在 deflateInit / deflateInit2 之后, deflate 之前調(diào)用.
            ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)):
            和 deflateInit 類似,使用默認參數(shù)初始化解壓器. zlib 或者 gzip 數(shù)據(jù)頭會被丟棄,如果需要保留數(shù)據(jù)頭信息應(yīng)該在 inflateInit2() 之后, inflate()之前,調(diào)用 inflateGetHeader() 提供一個 gzip 頭結(jié)構(gòu) struct gz_header,一旦 libzlib 讀取到一個完整的 gzip 頭就會把信息填入到這個結(jié)構(gòu)中, inflate() 返回后,檢查 gz_header 結(jié)構(gòu)的 done 字段, 1 表示數(shù)據(jù)頭讀取完畢; 0 表示正在解壓; -1 表示沒有 gzip 頭,對一個 zlib 格式的壓縮流使用這個函數(shù)就會得到 -1.
            ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)):
            解壓縮,和 deflate 的調(diào)用流程一樣,最后應(yīng)該以參數(shù) flush = Z_FINISH 調(diào)用 infate,返回 Z_STREAM_END 表示解壓縮完成,并且校驗和匹配.
            ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)):
            釋放 z_stream.
            ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, int  windowBits)):
            和 deflateInit2 對應(yīng),通常用相同的 windowBits 值就可以了.把 windowBits + 32 可以使解壓器自動識別 zlib 或者 gzip 包裝格式.
            libzlib 還提供以回調(diào)方式處理解壓縮的 API: inflateBackInit / inflateBack / inflateBackEnd.
            3.2 工具函數(shù)
            compress / compress2 / compressBound / uncompress 是對基本 API 的組合使用,也是如何調(diào)用基本 API 的范本,我們應(yīng)該仔細閱讀 compress.c 和 uncompr.c.
            可以用 compressBound() 來估算壓縮后的數(shù)據(jù)長度,但是沒有任何方法估算解壓后的原文長度,所以用戶應(yīng)該通過其它渠道得到原文長度,分配足夠的緩沖區(qū)以調(diào)用 uncompress().
            3.3 其它 API
            讀我的文章是不能直接通過復(fù)制粘貼來寫代碼的,但是看過之后應(yīng)該能理解 libzlib 的使用原理(至少我希望達到這個目的),不僅僅知道要調(diào)用哪些函數(shù),還要理解所以然.具體編寫代碼還是應(yīng)該查看 libzlib 的文檔.
            4. Windows平臺下編譯
            既然是一個自由庫,我們還是下載 zlib 的源碼自己編譯,不要使用已經(jīng)編譯好的 DLL 庫,訪問 http://www.zlib.net/ 下載 ".zip" 格式的源碼包.
            打開 "README", 看到 "For Windows, use one of the special makefiles in win32/ or contrib/vstudio/ ." 切換到 contrib/vstudio/ 目錄,又發(fā)現(xiàn)一個 readme.txt 是關(guān)于不同版本的 VS 的一些細節(jié), 根據(jù)自己安裝的 VS 版本打開對于的工程文件(耐心閱讀 readme 很有必要,少走好多彎路.)
            方法1 使用 Visual Studio IDE: 由于我已經(jīng)安裝了 Visual Studio 2013 所以直接用 VS2013 打開 /contrib/vstudio/vc11/zlibvc.sln (這其實是 VS2012 的工程文件). 編譯 "zlibvc" 這是最基本的動態(tài)庫 DLL 工程,提示 3 個鏈接錯誤:
            1>match686.obj : error LNK2026: module unsafe for SAFESEH image.
            1>inffas32.obj : error LNK2026: module unsafe for SAFESEH image.
            1>.\zlibvc.def(4): fatal error LNK1118: syntax error in 'VERSION' statement
            先看看 LNK1118 錯誤: 在 StackOverflow (http://stackoverflow.com/questions/20021950/def-file-syntax-error-in-visual-studio-2012) 看到原來是 .def 中 VERSION 定義的語法改了(其實是修正了)只允許兩個數(shù)字的版本號:主版本號和副版本號.所以要么把 "VERSION 1.2.8" 改為兩個數(shù)字的版本號,要么創(chuàng)建一個 VERSION 類型的資源.事實上 version 1.2.8 的資源已經(jīng)包含在工程中,所以我們只要簡單的在 zlibvc.def 中把 VERSION 這行注釋掉就好了.
            再看 LNK2026 錯誤: SAFESEH 按字面上理解應(yīng)該是 SAFE SEH - 安全的結(jié)構(gòu)化異常處理, SEH 是windows平臺的結(jié)構(gòu)化異常處理機制,通過擴展 C 編譯器 __try, __finally 關(guān)鍵字來控制程序流程<<Windows核心編程>>有相關(guān)內(nèi)容介紹. libzlib 大概是不會使用 SEH 的.也許是因為 VS2013 把這個選項的默認設(shè)置改變了,具體什么原因?qū)е虏患嫒菸也恢?總之把 SAFESEH 關(guān)閉吧: 工程屬性 -> Linker -> Advanced -> Image Has Safe Exception Handlers 改為 NO,重新編譯,發(fā)現(xiàn) testzlib 也有同樣的問題,關(guān)閉 SAFESEH 再次編譯就好了.
            庫文件: zlibwapi.lib, zlibwapi.dll, zlibstat.lib(靜態(tài)庫)
            頭文件: zconf.h, zlib.h
            微軟這種更新一個版本就使舊工程無法編譯鏈接的做法真是不可理喻.
            方法2 使用 nmake, 把 win32/Makefile.msc 復(fù)制到上一層源碼目錄,啟動 "Developer Command Prompt for VS2013" (在開始菜單里), 用 CD 命令切換到 zlib 1.2.8 源碼目錄,輸入 "nmake /f Makefile.msc" 編譯完成.
            庫文件: zdll.lib, zlib1.dll, zlib.lib(靜態(tài)庫)
            頭文件: zconf.h, zlib.h
            5. demo
              1 #include <stdio.h>
              2 #include <string.h>
              3 #include <assert.h>
              4 
              5 extern "C"
              6 {
              7     #include "zlib.h"
              8 }
              9 #pragma comment(lib, "zlib.lib")
             10 
             11 int dump_buffer(const Bytef* buf, size_t len)
             12 {
             13     for(size_t i = 0; i < len; ++i)
             14     {
             15         printf("%02x", buf[i]);
             16     }
             17     return 0;
             18 }
             19 
             20 int _tmain(int argc, _TCHAR* argv[])
             21 {
             22     const char* inBuf = "1234,abcd,ABCD,^#@!.";
             23     Bytef outBuf[1024= {0};
             24     Bytef restoreBuf[1024= {0};
             25     int outLen = 0;
             26     int restoreLen = 0;
             27     int err = 0;
             28     z_stream stream;
             29     int fmt = 2// 0: zlib; 1: gzip; 2: raw
             30 
             31     printf("source string:%s\r\n", inBuf);
             32 
             33     // 壓縮
             34     stream.next_in = (z_const Bytef *)inBuf;
             35     stream.avail_in = (uInt)strlen(inBuf);
             36 
             37     stream.next_out = (Bytef *)outBuf;
             38     stream.avail_out = 1024;
             39 
             40     stream.zalloc = (alloc_func)0;
             41     stream.zfree = (free_func)0;
             42     stream.opaque = (voidpf)0;
             43 
             44     if(0 == fmt)
             45     {
             46         // zlib
             47         err = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
             48         assert(Z_OK == err);
             49 
             50         err = deflate(&stream, Z_FINISH);
             51         assert(err == Z_STREAM_END);
             52 
             53         outLen = stream.total_out;
             54 
             55         err = deflateEnd(&stream);
             56 
             57         printf("zlib string(HEX):");
             58     }
             59     else if(1 == fmt)
             60     {
             61         // gzip
             62         err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + 168, Z_DEFAULT_STRATEGY);
             63         assert(Z_OK == err);
             64 
             65         err = deflate(&stream, Z_FINISH);
             66         assert(err == Z_STREAM_END);
             67 
             68         outLen = stream.total_out;
             69 
             70         err = deflateEnd(&stream);
             71 
             72         printf("gzip string(HEX):");
             73     }
             74     else if(2 == fmt)
             75     {
             76         // raw
             77         err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS * -18, Z_DEFAULT_STRATEGY);
             78         assert(Z_OK == err);
             79 
             80         err = deflate(&stream, Z_FINISH);
             81         assert(err == Z_STREAM_END);
             82 
             83         outLen = stream.total_out;
             84 
             85         err = deflateEnd(&stream);
             86 
             87         printf("raw deflate string(HEX):");
             88     }
             89     else
             90     {
             91         assert(0);
             92     }
             93 
             94     dump_buffer(outBuf, outLen);
             95     printf("\r\n");
             96 
             97     // 解壓縮
             98     stream.next_in = (z_const Bytef *)outBuf;
             99     stream.avail_in = (uInt)outLen;
            100 
            101     stream.next_out = (Bytef *)restoreBuf;
            102     stream.avail_out = 1024;
            103 
            104     stream.zalloc = (alloc_func)0;
            105     stream.zfree = (free_func)0;
            106     stream.opaque = (voidpf)0;
            107 
            108     if(0 == fmt)
            109     {
            110         // zlib
            111         err = inflateInit(&stream);
            112         assert(Z_OK == err);
            113 
            114         err = inflate(&stream, Z_FINISH);
            115         assert(err == Z_STREAM_END);
            116 
            117         restoreLen = stream.total_out;
            118 
            119         err = inflateEnd(&stream);
            120     }
            121     else if(1 == fmt)
            122     {
            123         // gzip
            124         err = inflateInit2(&stream, MAX_WBITS + 16);
            125         assert(Z_OK == err);
            126 
            127         err = inflate(&stream, Z_FINISH);
            128         assert(err == Z_STREAM_END);
            129 
            130         restoreLen = stream.total_out;
            131 
            132         err = inflateEnd(&stream);
            133     }
            134     else if(2 == fmt)
            135     {
            136         // raw
            137         err = inflateInit2(&stream, MAX_WBITS * -1);
            138         assert(Z_OK == err);
            139 
            140         err = inflate(&stream, Z_FINISH);
            141         assert(err == Z_STREAM_END);
            142 
            143         restoreLen = stream.total_out;
            144 
            145         err = inflateEnd(&stream);
            146     }
            147     else
            148     {
            149         assert(0);
            150     }
            151 
            152     printf("restored string:%s\r\n", (char*)restoreBuf);
            153 
            154     printf("Press Enter to continue");
            155     getchar();
            156     return err;
            157 }
            fmt 分別設(shè)置為 0, 1, 2 時的運行結(jié)果:
            source string:1234,abcd,ABCD,^#@!.
            zlib string(HEX):789c33343236d1494c4a4ed171747276d189537650d40300357804f3
            restored string:1234,abcd,ABCD,^#@!.
            source string:1234,abcd,ABCD,^#@!.
            gzip string(HEX):1f8b080000000000000b33343236d1494c4a4ed171747276d189537650d4030065d6b0c314000000
            restored string:1234,abcd,ABCD,^#@!.
            source string:1234,abcd,ABCD,^#@!.
            raw deflate string(HEX):33343236d1494c4a4ed171747276d189537650d40300
            restored string:1234,abcd,ABCD,^#@!.
            可以看到中間的壓縮數(shù)據(jù)都是相同的,只是頭尾不同.

            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            av国内精品久久久久影院| 日日噜噜夜夜狠狠久久丁香五月| 亚洲va久久久久| 国产AⅤ精品一区二区三区久久| 久久精品无码专区免费东京热| 久久无码中文字幕东京热| 中文字幕精品久久久久人妻| 国内精品久久久久国产盗摄| 国产69精品久久久久99尤物| 伊人久久精品线影院| 精品欧美一区二区三区久久久| 国产福利电影一区二区三区久久老子无码午夜伦不 | 久久精品一区二区影院| 亚洲国产精品久久66| 婷婷综合久久狠狠色99h| 色综合合久久天天综合绕视看 | 亚洲AV无码久久精品蜜桃| 精品多毛少妇人妻AV免费久久 | 77777亚洲午夜久久多喷| 欧洲人妻丰满av无码久久不卡| 精品熟女少妇a∨免费久久| 韩国三级大全久久网站| 国产亚洲美女精品久久久| 性做久久久久久免费观看| 伊人久久综合成人网| 久久99国产精品久久99| 久久国产免费| 久久国产成人午夜AV影院| 亚洲国产成人久久一区WWW| 欧美日韩精品久久免费| 久久精品国产99久久久 | 伊人久久大香线蕉精品| 久久99亚洲综合精品首页| 国产成人久久精品一区二区三区| 国产91久久精品一区二区| 久久久久九国产精品| 久久精品亚洲精品国产色婷| 久久国产视频网| 国产午夜免费高清久久影院 | 久久久无码一区二区三区| 国产福利电影一区二区三区久久久久成人精品综合 |