1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 12:32:21 +00:00

ch2-6: 实战 qsort 包装 完成

This commit is contained in:
chai2010 2018-04-27 14:06:41 +08:00
parent 84718a8e50
commit 58fb12e46e
9 changed files with 392 additions and 332 deletions

View File

@ -15,7 +15,7 @@
* [2.3. 类型转换](ch2-cgo/ch2-03-cgo-types.md) * [2.3. 类型转换](ch2-cgo/ch2-03-cgo-types.md)
* [2.4. 函数调用](ch2-cgo/ch2-04-func.md) * [2.4. 函数调用](ch2-cgo/ch2-04-func.md)
* [2.5. 内部机制](ch2-cgo/ch2-05-internal.md) * [2.5. 内部机制](ch2-cgo/ch2-05-internal.md)
* [2.6. 实战: 封装qsort(Doing)](ch2-cgo/ch2-06-qsort.md) * [2.6. 实战: 封装qsort](ch2-cgo/ch2-06-qsort.md)
* [2.7. CGO内存模型](ch2-cgo/ch2-07-memory.md) * [2.7. CGO内存模型](ch2-cgo/ch2-07-memory.md)
* [2.8. C++类包装](ch2-cgo/ch2-08-class.md) * [2.8. C++类包装](ch2-cgo/ch2-08-class.md)
* [2.9. 静态库和动态库](ch2-cgo/ch2-09-static-shared-lib.md) * [2.9. 静态库和动态库](ch2-cgo/ch2-09-static-shared-lib.md)

View File

@ -4,7 +4,7 @@ qsort快速排序函数是C语言的高阶函数支持用于自定义排序
## 认识qsort函数 ## 认识qsort函数
qosrt快速排序函数有`<stdlib.h>`标准库提供,函数的声明如下: qsort快速排序函数有`<stdlib.h>`标准库提供,函数的声明如下:
```c ```c
void qsort( void qsort(
@ -42,222 +42,295 @@ int main() {
} }
``` ```
## 用于Go固定类型数组的排序 其中`DIM(values)`宏用于几十数组元素的个数,`sizeof(values[0])`用于计算数组元素的大小。
cmp是用于排序时比较两个元素大小的回调函数。为了避免对全局名字空间的污染我们将cmp回调函数定义为仅当前文件内可方位的静态函数。
## 将qsort函数从Go包导出
为了方便Go语言的非CGO用户使用qsort函数我们需要将C语言的qsort函数包装为一个外部可以访问的Go函数。
用Go语言将qsort函数重新包装为`qsort.Sort`函数:
```go ```go
/* package qsort
#include <stdlib.h>
static int compare(const void* a, const void* b) { //typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
const int* pa = (int*)a;
const int* pb = (int*)b;
return *pa - *pb;
}
static void qsort_proxy(int* values, size_t len, size_t elemsize) {
qsort(values, len, sizeof(values[0]), compare);
}
*/
import "C" import "C"
import "unsafe" import "unsafe"
import "fmt"
func main() { func Sort(
values := []int32{ 42, 9, 101, 95, 27, 25 }; base unsafe.Pointer, num, size C.size_t,
C.qsort_proxy( cmp C.qsort_cmp_func_t,
(*C.int)(unsafe.Pointer(&values[0])), ) {
C.size_t(len(values)), C.qsort(base, num, size, cmp)
C.size_t(unsafe.Sizeof(values[0])),
)
fmt.Println(values)
} }
``` ```
因为 compare 函数固定了元素的大小,导致只能针对特点的数值类型排序 因为Go语言的CGO语言不好直接表达C语言的函数类型因此在C语言空间将比较函数类型重新定义为一个`qsort_cmp_func_t`类型
## 在Go中自传入比较函数 虽然Sort函数已经导出了但是对于qsort包之外的用户依然不能直角使用该函数——Sort函数的参数还包含了虚拟的C包提供的类型。
在CGO的内部机制一节中我们已经提过虚拟的C包下的任何名称其实都会被映射为包内的私有名字。比如`C.size_t`会被展开为`_Ctype_size_t``C.qsort_cmp_func_t`类型会被展开为`_Ctype_qsort_cmp_func_t`
被CGO处理后的Sort函数的类型如下
```go ```go
/* func Sort(
extern int go_qsort_compare(void* a, void* b); base unsafe.Pointer, num, size _Ctype_size_t,
cmp _Ctype_qsort_cmp_func_t,
static int compare(const void* a, const void* b) { )
return go_qsort_compare((void*)(a), (void*)(b));
}
*/
import "C"
//export go_qsort_compare
func go_qsort_compare(a, b unsafe.Pointer) C.int {
pa := (*C.int)(a)
pb := (*C.int)(b)
return C.int(*pa - *pb)
}
``` ```
这样将会导致包外部用于无法构造`_Ctype_size_t``_Ctype_qsort_cmp_func_t`类型的参数而无法使用Sort函数。因此导出的Sort函数的参数和返回值要避免对虚拟C包的依赖。
重新调整Sort函数的参数类型和实现如下
```go ```go
/* /*
#include <stdlib.h> #include <stdlib.h>
typedef int (*qsort_cmp_func_t)(const void* a, const void* b); typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
extern int go_qsort_compare(void* a, void* b);
*/ */
import "C" import "C"
import "unsafe"
func main() { type CompareFunc C.qsort_cmp_func_t
values := []int32{42, 9, 101, 95, 27, 25}
C.qsort(unsafe.Pointer(&values[0]), func Sort(base unsafe.Pointer, num, size int, cmp CompareFunc) {
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])), C.qsort(base, C.size_t(num), C.size_t(size), C.qsort_cmp_func_t(cmp))
(C.qsort_cmp_func_t)(unsafe.Pointer(C.go_qsort_compare)),
)
} }
``` ```
直接传入Go导出的比较函数需要进行一次类型转换因为参数少了const修饰 我们将虚拟C包中的类型通过Go语言类型代替在内部调用C函数时重新转型为C函数需要的类型。因此外部用户将不再依赖qsort包内的虚拟C包
以下代码展示的Sort函数的使用方式
```go ```go
package main
//extern int go_qsort_compare(void* a, void* b);
import "C" import "C"
import (
"fmt"
"unsafe"
qsort "."
)
//export go_qsort_compare //export go_qsort_compare
func go_qsort_compare(a, b unsafe.Pointer) C.int { func go_qsort_compare(a, b unsafe.Pointer) C.int {
return go_qsort_compare_info.fn(a, b) pa, pb := (*C.int)(a), (*C.int)(b)
return C.int(*pa - *pb)
} }
var go_qsort_compare_info struct {
fn func(a, b unsafe.Pointer) C.int
sync.RWMutex
}
```
```go
func main() { func main() {
values := []int32{42, 9, 101, 95, 27, 25} values := []int32{42, 9, 101, 95, 27, 25}
go_qsort_compare_info.Lock() qsort.Sort(unsafe.Pointer(&values[0]),
defer go_qsort_compare_info.Unlock() len(values), int(unsafe.Sizeof(values[0])),
go_qsort_compare_info.fn = func(a, b unsafe.Pointer) C.int { qsort.CompareFunc(C.go_qsort_compare),
pa := (*C.int)(a) )
pb := (*C.int)(b) fmt.Println(values)
return C.int(*pa - *pb)
}
C.qsort(unsafe.Pointer(&values[0]),
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])),
(C.qsort_cmp_func_t)(unsafe.Pointer(C.go_qsort_compare)),
)
} }
``` ```
为了使用Sort函数我们需要将Go语言的切片取首地址、元素个数、元素大小等信息作为调用参数同时还需要提过一个C语言规格的比较函数。
其中go_qsort_compare是用Go语言实现的并导出到C语言空间的函数用于qsort排序时的比较函数。
目前已经实现了对C语言的qsort初步包装并且可以通过包的方式被其它用户使用。但是`qsort.Sort`函数已经有很多不便使用之处用户要提过C语言的比较函数这对许多Go语言用户是一个挑战。下一步我们将继续改进qsort函数的包装函数尝试通过闭包函数代替C语言的比较函数。
消除用户对CGO代码的直角依赖。
## 改进:闭包函数作为比较函数
在改进之前我们先回顾下Go语言sort包自带的排序函数的接口
```go ```go
func qsort(values []int32, fn func(a, b unsafe.Pointer) C.int) { func Slice(slice interface{}, less func(i, j int) bool)
go_qsort_compare_info.Lock()
defer go_qsort_compare_info.Unlock()
go_qsort_compare_info.fn = fn
C.qsort(
unsafe.Pointer(&values[0]),
C.size_t(len(values)),
C.size_t(unsafe.Sizeof(values[0])),
(C.qsort_cmp_func_t)(unsafe.Pointer(C.go_qsort_compare)),
)
}
``` ```
标准库的sort.Slice因为支持通过闭包函数指定比较函数对切片的排序非常简单
## Go比较函数类型的简化
## 适配更多数组类型
```go ```go
func qsort(slice interface{}, fn func(a, b unsafe.Pointer) C.int) { import "sort"
sv := reflect.ValueOf(slice)
if sv.Kind() != reflect.Slice {
panic("not slice type")
}
go_qsort_compare_info.Lock() func main() {
defer go_qsort_compare_info.Unlock() values := []int32{42, 9, 101, 95, 27, 25}
go_qsort_compare_info.fn = fn
C.qsort( sort.Slice(values, less func(i, j int) bool {
unsafe.Pointer(unsafe.Pointer(sv.Index(0).Addr().Pointer())), return values[i] < values[j]
C.size_t(sv.Len()), C.size_t(sv.Type().Elem().Size()), })
(C.qsort_cmp_func_t)(unsafe.Pointer(C.go_qsort_compare)),
) fmt.Println(values)
} }
``` ```
我们也尝试将C语言的qsort函数包装为以下格式的Go语言函数
```go ```go
func qsort(slice interface{}, fn func(a, b int) int) { package qsort
...
func Sort(base unsafe.Pointer, num, size int, cmp func(a, b unsafe.Pointer) int)
```
闭包函数函数无法导出为C语言函数因此无法之间将闭包函数传入C语言的qsort函数。
为此我们可以用Go构造一个可以导出为C语言的代理函数然后通过一个全局变量临时保存当前的闭包比较函数。
代码如下:
```go
var go_qsort_compare_info struct {
fn func(a, b unsafe.Pointer) int
sync.Mutex
}
//export _cgo_qsort_compare
func _cgo_qsort_compare(a, b unsafe.Pointer) C.int {
return C.int(go_qsort_compare_info.fn(a, b))
} }
``` ```
其中导出的C语言函数`_cgo_qsort_compare`是公用的qsort比较函数内部通过`go_qsort_compare_info.fn`来调用当前的闭包比较函数。
新的Sort包装函数实现如下
```go ```go
/* /*
#include <stdlib.h> #include <stdlib.h>
extern int go_qsort_compare(void* a, void* b); typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
extern void go_qsort_compare_save_base(void* base); extern int _cgo_qsort_compare(void* a, void* b);
static void qsort_proxy(
void* base, size_t num, size_t size,
int (*compar)(const void* a, const void* b)
) {
go_qsort_compare_save_base(base); // 保存数组地址
qsort(base, num, size, compar);
}
*/ */
import "C" import "C"
```
```go func Sort(base unsafe.Pointer, num, size int, cmp func(a, b unsafe.Pointer) int) {
//export go_qsort_compare_save_base go_qsort_compare_info.Lock()
func go_qsort_compare_save_base(base unsafe.Pointer) { defer go_qsort_compare_info.Unlock()
go_qsort_compare_info.base = uintptr(base)
}
var go_qsort_compare_info struct { go_qsort_compare_info.fn = cmp
base uintptr
elemsize uintptr C.qsort(base, C.size_t(num), C.size_t(size),
fn func(a, b int) int C.qsort_cmp_func_t(C._cgo_qsort_compare),
sync.RWMutex )
} }
``` ```
```go 每次排序前对全局的go_qsort_compare_info变量加锁同时将当前的闭包函数保存到全局变量然后调用C语言的qsort函数。
//export go_qsort_compare
func go_qsort_compare(a, b unsafe.Pointer) C.int {
var (
// array memory is locked
base = go_qsort_compare_info.base
elemsize = go_qsort_compare_info.elemsize
)
i := int((uintptr(a) - base) / elemsize) 基于新包装的函数,我们可以简化之前的排序代码:
j := int((uintptr(b) - base) / elemsize)
return C.int(go_qsort_compare_info.fn(i, j))
}
```
```go ```go
func main() { func main() {
values := []int64{42, 9, 101, 95, 27, 25} values := []int32{42, 9, 101, 95, 27, 25}
qsort(values, func(i, j int) int { qsort.Sort(unsafe.Pointer(&values[0]), len(values), int(unsafe.Sizeof(values[0])),
return int(values[i] - values[j]) func(a, b unsafe.Pointer) int {
}) pa, pb := (*int32)(a), (*int32)(b)
} return int(*pa - *pb)
},
)
func qsort(slice interface{}, fn func(a, b int) int) { fmt.Println(values)
...
} }
``` ```
![](../images/ch2-qsort-v2.uml.png) 现在排序不再需要通过CGO实现C语言版本的比较函数了可以传入Go语言闭包函数作为比较函数。
但是导入的排序函数依然依赖unsafe包这是违背Go语言编程习惯的。
## 改进消除用户对unsafe包的依赖
前一个版本的qsort.Sort包装函数已经比最初的C语言版本的qsort易用很多但是依然保留了很多C语言底层数据结构的细节。
现在我们将继续改进包装函数尝试消除对unsafe包的依赖并实现一个类似标准库中sort.Slice的排序函数。
新的包装函数声明如下:
```go
package qsort
func Slice(slice interface{}, less func(a, b int) bool)
```
首先我们将slice作为接口类型参数传入这样可以适配不同的切片类型。
然后切片的首个元素的地址、元素个数和元素大小可以通过reflect反射包从切片中获取。
为了保存必要的排序上下文信息我们需要在全局包变量增加要排序数组的地址、元素个数和元素大小等信息比较函数改为less
```go
var go_qsort_compare_info struct {
base unsafe.Pointer
elemnum int
elemsize int
less func(a, b int) bool
sync.Mutex
}
```
同样比较函数需要根据元素指针、排序数组的开始地址和元素的大小计算出元素对应数组的索引下标,
然后根据less函数的比较结果返回qsort函数需要格式的比较结果。
```go
//export _cgo_qsort_compare
func _cgo_qsort_compare(a, b unsafe.Pointer) C.int {
var (
// array memory is locked
base = uintptr(go_qsort_compare_info.base)
elemsize = uintptr(go_qsort_compare_info.elemsize)
)
i := int((uintptr(a) - base) / elemsize)
j := int((uintptr(b) - base) / elemsize)
switch {
case go_qsort_compare_info.less(i, j): // v[i] < v[j]
return -1
case go_qsort_compare_info.less(j, i): // v[i] > v[j]
return +1
default:
return 0
}
}
```
新的Slice函数的实现如下
```go
func Slice(slice interface{}, less func(a, b int) bool) {
sv := reflect.ValueOf(slice)
if sv.Kind() != reflect.Slice {
panic(fmt.Sprintf("qsort called with non-slice value of type %T", slice))
}
if sv.Len() == 0 {
return
}
go_qsort_compare_info.Lock()
defer go_qsort_compare_info.Unlock()
defer func() {
go_qsort_compare_info.base = nil
go_qsort_compare_info.elemnum = 0
go_qsort_compare_info.elemsize = 0
go_qsort_compare_info.less = nil
}()
// baseMem = unsafe.Pointer(sv.Index(0).Addr().Pointer())
// baseMem maybe moved, so must saved after call C.fn
go_qsort_compare_info.base = unsafe.Pointer(sv.Index(0).Addr().Pointer())
go_qsort_compare_info.elemnum = sv.Len()
go_qsort_compare_info.elemsize = int(sv.Type().Elem().Size())
go_qsort_compare_info.less = less
C.qsort(
go_qsort_compare_info.base,
C.size_t(go_qsort_compare_info.elemnum),
C.size_t(go_qsort_compare_info.elemsize),
C.qsort_cmp_func_t(C._cgo_qsort_compare),
)
}
```
首先需要判断传入的接口类型必须是切片类型。然后通过反射获取qsort函数需要的切片信息并调用C语言的qsort函数。
为了避免在排序过程中,排序数组的上下文信息`go_qsort_compare_info`被修改,我们进行了全局加锁。
因此目前版本的qsort.Slice函数是无法并发执行的读者可以自己尝试改进这个限制。

View File

@ -1,52 +1,32 @@
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>. // Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ // License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// +build ignore
package main package main
/* //extern int go_qsort_compare(void* a, void* b);
#include <stdlib.h>
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
// disable static
int qsort_compare_callback(const void* a, const void* b) {
const int* pa = (int*)a;
const int* pb = (int*)b;
return *pa - *pb;
}
static int compare(const void* a, const void* b) {
const int* pa = (int*)a;
const int* pb = (int*)b;
return *pa - *pb;
}
static qsort_cmp_func_t get_compare_ptr() {
return compare;
}
*/
import "C" import "C"
import "unsafe" import (
import "fmt" "fmt"
"unsafe"
func qsort(base unsafe.Pointer, num, size C.size_t, cmp C.qsort_cmp_func_t) { qsort "."
C.qsort(base, num, size, C.qsort_cmp_func_t(cmp)) )
}
func main() { func main() {
values := []int32{42, 9, 101, 95, 27, 25} values := []int32{42, 9, 101, 95, 27, 25}
qsort(unsafe.Pointer(&values[0]), qsort.Sort(unsafe.Pointer(&values[0]),
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])), len(values), int(unsafe.Sizeof(values[0])),
C.get_compare_ptr(), // static callback must use proxy get qsort.CompareFunc(C.go_qsort_compare),
)
fmt.Println(values)
values = []int32{42, 9, 101, 95, 27, 25}
qsort(unsafe.Pointer(&values[0]),
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])),
C.qsort_cmp_func_t(unsafe.Pointer(C.qsort_compare_callback)),
) )
fmt.Println(values) fmt.Println(values)
} }
//export go_qsort_compare
func go_qsort_compare(a, b unsafe.Pointer) C.int {
pa, pb := (*C.int)(a), (*C.int)(b)
return C.int(*pa - *pb)
}

View File

@ -0,0 +1,19 @@
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package qsort
/*
#include <stdlib.h>
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
*/
import "C"
import "unsafe"
type CompareFunc C.qsort_cmp_func_t
func Sort(base unsafe.Pointer, num, size int, cmp CompareFunc) {
C.qsort(base, C.size_t(num), C.size_t(size), C.qsort_cmp_func_t(cmp))
}

View File

@ -1,38 +1,25 @@
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>. // Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ // License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// +build ignore
package main package main
/*
#include <stdlib.h>
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
extern int go_qsort_compare(void* a, void* b);
*/
import "C"
import ( import (
"fmt" "fmt"
"unsafe" "unsafe"
qsort "."
) )
//export go_qsort_compare
func go_qsort_compare(a, b unsafe.Pointer) C.int {
pa := (*C.int)(a)
pb := (*C.int)(b)
return C.int(*pa - *pb)
}
func qsort(base unsafe.Pointer, num, size C.size_t, cmp C.qsort_cmp_func_t) {
C.qsort(base, num, size, C.qsort_cmp_func_t(cmp))
}
func main() { func main() {
values := []int32{42, 9, 101, 95, 27, 25} values := []int32{42, 9, 101, 95, 27, 25}
qsort(unsafe.Pointer(&values[0]), qsort.Sort(unsafe.Pointer(&values[0]), len(values), int(unsafe.Sizeof(values[0])),
C.size_t(len(values)), C.size_t(unsafe.Sizeof(values[0])), func(a, b unsafe.Pointer) int {
C.qsort_cmp_func_t(unsafe.Pointer(C.go_qsort_compare)), pa, pb := (*int32)(a), (*int32)(b)
return int(*pa - *pb)
},
) )
fmt.Println(values) fmt.Println(values)

View File

@ -0,0 +1,37 @@
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package qsort
/*
#include <stdlib.h>
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
extern int _cgo_qsort_compare(void* a, void* b);
*/
import "C"
import (
"sync"
"unsafe"
)
var go_qsort_compare_info struct {
fn func(a, b unsafe.Pointer) int
sync.Mutex
}
//export _cgo_qsort_compare
func _cgo_qsort_compare(a, b unsafe.Pointer) C.int {
return C.int(go_qsort_compare_info.fn(a, b))
}
func Sort(base unsafe.Pointer, num, size int, cmp func(a, b unsafe.Pointer) int) {
go_qsort_compare_info.Lock()
defer go_qsort_compare_info.Unlock()
go_qsort_compare_info.fn = cmp
C.qsort(base, C.size_t(num), C.size_t(size),
C.qsort_cmp_func_t(C._cgo_qsort_compare),
)
}

View File

@ -1,54 +1,22 @@
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>. // Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ // License: https://creativecommons.org/licenses/by-nc-sa/4.0/
// +build ignore
package main package main
import ( import (
"fmt" "fmt"
qsort "."
) )
/*
#include <stdlib.h>
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
extern int go_qsort_compare(void* a, void* b);
*/
import "C"
import (
"sync"
"unsafe"
)
var go_qsort_compare_info struct {
fn func(a, b unsafe.Pointer) int
sync.Mutex
}
//export go_qsort_compare
func go_qsort_compare(a, b unsafe.Pointer) C.int {
return C.int(go_qsort_compare_info.fn(a, b))
}
func qsort(base unsafe.Pointer, num, size int, cmp func(a, b unsafe.Pointer) int) {
go_qsort_compare_info.Lock()
defer go_qsort_compare_info.Unlock()
go_qsort_compare_info.fn = cmp
C.qsort(base, C.size_t(num), C.size_t(size),
C.qsort_cmp_func_t(C.go_qsort_compare),
)
}
func main() { func main() {
values := []int32{42, 9, 101, 95, 27, 25} values := []int64{42, 9, 101, 95, 27, 25}
qsort(unsafe.Pointer(&values[0]), len(values), int(unsafe.Sizeof(values[0])), qsort.Slice(values, func(i, j int) bool {
func(a, b unsafe.Pointer) int { return values[i] < values[j]
pa, pb := (*C.int)(a), (*C.int)(b) })
return int(*pa - *pb)
},
)
fmt.Println(values) fmt.Println(values)
} }

View File

@ -0,0 +1,83 @@
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package qsort
/*
#include <stdlib.h>
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
extern int _cgo_qsort_compare(void* a, void* b);
*/
import "C"
import (
"fmt"
"reflect"
"sync"
"unsafe"
)
var go_qsort_compare_info struct {
base unsafe.Pointer
elemnum int
elemsize int
less func(a, b int) bool
sync.Mutex
}
//export _cgo_qsort_compare
func _cgo_qsort_compare(a, b unsafe.Pointer) C.int {
var (
// array memory is locked
base = uintptr(go_qsort_compare_info.base)
elemsize = uintptr(go_qsort_compare_info.elemsize)
)
i := int((uintptr(a) - base) / elemsize)
j := int((uintptr(b) - base) / elemsize)
switch {
case go_qsort_compare_info.less(i, j): // v[i] < v[j]
return -1
case go_qsort_compare_info.less(j, i): // v[i] > v[j]
return +1
default:
return 0
}
}
func Slice(slice interface{}, less func(a, b int) bool) {
sv := reflect.ValueOf(slice)
if sv.Kind() != reflect.Slice {
panic(fmt.Sprintf("qsort called with non-slice value of type %T", slice))
}
if sv.Len() == 0 {
return
}
go_qsort_compare_info.Lock()
defer go_qsort_compare_info.Unlock()
defer func() {
go_qsort_compare_info.base = nil
go_qsort_compare_info.elemnum = 0
go_qsort_compare_info.elemsize = 0
go_qsort_compare_info.less = nil
}()
// baseMem = unsafe.Pointer(sv.Index(0).Addr().Pointer())
// baseMem maybe moved, so must saved after call C.fn
go_qsort_compare_info.base = unsafe.Pointer(sv.Index(0).Addr().Pointer())
go_qsort_compare_info.elemnum = sv.Len()
go_qsort_compare_info.elemsize = int(sv.Type().Elem().Size())
go_qsort_compare_info.less = less
C.qsort(
go_qsort_compare_info.base,
C.size_t(go_qsort_compare_info.elemnum),
C.size_t(go_qsort_compare_info.elemsize),
C.qsort_cmp_func_t(C._cgo_qsort_compare),
)
}

View File

@ -1,87 +0,0 @@
// Copyright © 2018 ChaiShushan <chaishushan{AT}gmail.com>.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
package main
/*
#include <stdlib.h>
typedef int (*qsort_cmp_func_t)(const void* a, const void* b);
extern int go_qsort_compare(void* a, void* b);
*/
import "C"
import (
"fmt"
"reflect"
"sync"
"unsafe"
)
var go_qsort_compare_info struct {
slice interface{}
base uintptr
elemsize uintptr
fn func(a, b int) int
sync.Mutex
}
//export go_qsort_compare_save_base
func go_qsort_compare_save_base(base unsafe.Pointer) {
go_qsort_compare_info.base = uintptr(base)
}
//export go_qsort_compare
func go_qsort_compare(a, b unsafe.Pointer) C.int {
if go_qsort_compare_info.base == 0 {
sv := reflect.ValueOf(go_qsort_compare_info.slice)
go_qsort_compare_info.base = uintptr(unsafe.Pointer(sv.Index(0).Addr().Pointer()))
}
var (
// array memory is locked
base = go_qsort_compare_info.base
elemsize = go_qsort_compare_info.elemsize
)
i := int((uintptr(a) - base) / elemsize)
j := int((uintptr(b) - base) / elemsize)
return C.int(go_qsort_compare_info.fn(i, j))
}
func qsort(slice interface{}, fn func(a, b int) int) {
sv := reflect.ValueOf(slice)
if sv.Kind() != reflect.Slice {
panic(fmt.Sprintf("qsort called with non-slice value of type %T", slice))
}
if sv.Len() == 0 {
return
}
go_qsort_compare_info.Lock()
defer go_qsort_compare_info.Unlock()
// baseMem = unsafe.Pointer(sv.Index(0).Addr().Pointer())
// baseMem maybe moved, so must saved after call C.fn
go_qsort_compare_info.slice = slice
go_qsort_compare_info.elemsize = sv.Type().Elem().Size()
go_qsort_compare_info.fn = fn
C.qsort(
unsafe.Pointer(sv.Index(0).Addr().Pointer()),
C.size_t(sv.Len()), C.size_t(sv.Type().Elem().Size()),
C.qsort_cmp_func_t(C.go_qsort_compare),
)
}
func main() {
values := []int64{42, 9, 101, 95, 27, 25}
qsort(values, func(i, j int) int {
return int(values[i] - values[j])
})
fmt.Println(values)
}