make loop

This commit is contained in:
chai2010
2015-12-26 20:05:30 +08:00
parent 82ec0c025d
commit e15e88dad7
74 changed files with 207 additions and 207 deletions

View File

@@ -4,7 +4,7 @@ Go程序可能會遇到要訪問C語言的某些硬件驅動函數的場景
在本節中我們將構建一個簡易的數據壓縮程序使用了一個Go語言自帶的叫cgo的用於支援C語言函數調用的工具。這類工具一般被稱爲 *foreign-function interfaces* 簡稱ffi, 併且在類似工具中cgo也不是唯一的。SWIG http://swig.org 是另一個類似的且被廣泛使用的工具SWIG提供了很多複雜特性以支援C++的特性但SWIG併不是我們要討論的主題。
在標準庫的`compress/...`子包有很多流行的壓縮算法的編碼和解碼實現包括流行的LZW壓縮算法Unix的compress命令用的算法和DEFLATE壓縮算法GNU gzip命令用的算法。這些包的API的細節雖然有些差異但是它們都提供了針對 io.Writer類型輸的壓縮接口和提供了針對io.Reader類型輸入的解壓縮接口。例如
在標準庫的`compress/...`子包有很多流行的壓縮算法的編碼和解碼實現包括流行的LZW壓縮算法Unix的compress命令用的算法和DEFLATE壓縮算法GNU gzip命令用的算法。這些包的API的細節雖然有些差異但是它們都提供了針對 io.Writer類型輸的壓縮接口和提供了針對io.Reader類型輸入的解壓縮接口。例如
```Go
package gzip // compress/gzip
@@ -16,7 +16,7 @@ bzip2壓縮算法是基於優雅的Burrows-Wheeler變換算法運行速度
如果是比較小的C語言庫我們完全可以用純Go語言重新實現一遍。如果我們對性能也沒有特殊要求的話我們還可以用os/exec包的方法將C編寫的應用程序作爲一個子進程運行。隻有當你需要使用複雜而且性能更高的底層C接口時就是使用cgo的場景了譯註用os/exec包調用子進程的方法會導致程序運行時依賴那個應用程序。下面我們將通過一個例子講述cgo的具體用法。
譯註:本章采用的代碼都是最新的。因爲之前已經版的書中包含的代碼隻能在Go1.5之前使用。從Go1.6開始Go語言已經明確規定了哪些Go語言指針可以之間傳入C語言函數。新代碼重點是增加了bz2alloc和bz2free的兩個函數用於bz_stream對象空間的申請和釋放操作。下面是新代碼中增加的註釋説明這個問題
譯註:本章采用的代碼都是最新的。因爲之前已經版的書中包含的代碼隻能在Go1.5之前使用。從Go1.6開始Go語言已經明確規定了哪些Go語言指針可以之間傳入C語言函數。新代碼重點是增加了bz2alloc和bz2free的兩個函數用於bz_stream對象空間的申請和釋放操作。下面是新代碼中增加的註釋説明這個問題
```Go
// The version of this program that appeared in the first and second
@@ -37,7 +37,7 @@ bzip2壓縮算法是基於優雅的Burrows-Wheeler變換算法運行速度
// pointers to Go variables.
```
要使用libbzip2我們需要先構建一個bz_stream結構體用於保持輸入和輸緩存。然後有三個函數BZ2_bzCompressInit用於初始化緩存BZ2_bzCompress用於將輸入緩存的數據壓縮到輸緩存BZ2_bzCompressEnd用於釋放不需要的緩存。目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組合在一起的。)
要使用libbzip2我們需要先構建一個bz_stream結構體用於保持輸入和輸緩存。然後有三個函數BZ2_bzCompressInit用於初始化緩存BZ2_bzCompress用於將輸入緩存的數據壓縮到輸緩存BZ2_bzCompressEnd用於釋放不需要的緩存。目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組合在一起的。)
我們可以在Go代碼中直接調用BZ2_bzCompressInit和BZ2_bzCompressEnd但是對於BZ2_bzCompress我們將定義一個C語言的包裝函數用它完成眞正的工作。下面是C代碼對應一個獨立的文件。
@@ -106,7 +106,7 @@ func NewWriter(out io.Writer) io.WriteCloser {
在cgo註釋中還可以包含#cgo指令用於給C語言工具鏈指定特殊的參數。例如CFLAGS和LDFLAGS分别對應傳給C語言編譯器的編譯參數和鏈接器參數使它們可以特定目録找到bzlib.h頭文件和libbz2.a庫文件。這個例子假設你已經在/usr目録成功安裝了bzip2庫。如果bzip2庫是安裝在不同的位置你需要更新這些參數。
NewWriter函數通過調用C語言的BZ2_bzCompressInit函數來初始化stream中的緩存。在writer結構中還包括了另一個buffer用於輸緩存。
NewWriter函數通過調用C語言的BZ2_bzCompressInit函數來初始化stream中的緩存。在writer結構中還包括了另一個buffer用於輸緩存。
下面是Write方法的實現返迴成功壓縮數據的大小主體是一個循環中調用C語言的bz2compress函數實現的。從代碼可以看到Go程序可以訪問C語言的bz_stream、char和uint類型還可以訪問bz2compress等函數甚至可以訪問C語言中像BZ_RUN那樣的宏定義全部都是以C.x語法訪問。其中C.uint類型和Go語言的uint類型併不相同卽使它們具有相同的大小也是不同的類型。
@@ -132,9 +132,9 @@ func (w *writer) Write(data []byte) (int, error) {
}
```
在循環的每次迭代中向bz2compress傳入數據的地址和剩餘部分的長度還有輸緩存w.outbuf的地址和容量。這兩個長度信息通過它們的地址傳入而不是值傳入因爲bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值。每個塊壓縮後的數據被寫入到底層的io.Writer。
在循環的每次迭代中向bz2compress傳入數據的地址和剩餘部分的長度還有輸緩存w.outbuf的地址和容量。這兩個長度信息通過它們的地址傳入而不是值傳入因爲bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值。每個塊壓縮後的數據被寫入到底層的io.Writer。
Close方法和Write方法有着類似的結構通過一個循環將剩餘的壓縮數據刷新到輸緩存。
Close方法和Write方法有着類似的結構通過一個循環將剩餘的壓縮數據刷新到輸緩存。
```Go
// Close flushes the compressed data and closes the stream.
@@ -162,7 +162,7 @@ func (w *writer) Close() error {
}
```
壓縮完成後Close方法用了defer函數確保函數退前調用C.BZ2_bzCompressEnd和C.bz2free釋放相關的C語言運行時資源。此刻w.stream指針將不再有效我們將它設置爲nil以保證安全然後在每個方法中增加了nil檢測以防止用戶在關閉後依然錯誤使用相關方法。
壓縮完成後Close方法用了defer函數確保函數退前調用C.BZ2_bzCompressEnd和C.bz2free釋放相關的C語言運行時資源。此刻w.stream指針將不再有效我們將它設置爲nil以保證安全然後在每個方法中增加了nil檢測以防止用戶在關閉後依然錯誤使用相關方法。
上面的實現中不僅僅寫是非併發安全的甚至併發調用Close和Write方法也可能導致程序的的崩潰。脩複這個問題是練習13.3的內容。