diff --git a/.gitignore b/.gitignore index 93456099d..8470fac8f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ .settings/ .vscode/ vender/ -log/ composer.lock gitpush.sh pkg/ diff --git a/README_ZH.MD b/README_ZH.MD index a8b2b1062..59bb9de75 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -4,7 +4,7 @@ [![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf/g#pkg-subdirectories) [![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf) -[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf)](https://goreportcard.com/report/github.com/gogf/gf) +[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf) [![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master) [![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf) [![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf) diff --git a/g/container/garray/garray_normal_int.go b/g/container/garray/garray_normal_int.go index 2be98a810..339063ddf 100644 --- a/g/container/garray/garray_normal_int.go +++ b/g/container/garray/garray_normal_int.go @@ -9,11 +9,12 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" ) type IntArray struct { @@ -266,31 +267,83 @@ func (a *IntArray) PopRights(size int) []int { // Range picks and returns items by range, like array[start:end]. // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. -func (a *IntArray) Range(start, end int) []int { +// +// If is negative, then the offset will start from the end of array. +// If is omitted, then the sequence will have everything from start up +// until the end of the array. +func (a *IntArray) Range(start int, end ...int) []int { a.mu.RLock() defer a.mu.RUnlock() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]int)(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]int, end-start) - copy(array, a.array[start:end]) + array = make([]int, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and parameters. +// If in concurrent safe usage, it returns a copy of the slice; else a pointer. +// +// If offset is non-negative, the sequence will start at that offset in the array. +// If offset is negative, the sequence will start that far from the end of the array. +// +// If length is given and is positive, then the sequence will have up to that many elements in it. +// If the array is shorter than the length, then only the available array elements will be present. +// If length is given and is negative then the sequence will stop that many elements from the end of the array. +// If it is omitted, then the sequence will have everything from offset up until the end of the array. +// +// Any possibility crossing the left border of array, it will fail. +func (a *IntArray) SubSlice(offset int, length ...int) []int { + a.mu.RLock() + defer a.mu.RUnlock() + size := len(a.array) + if len(length) > 0 { + size = length[0] + } + if offset > len(a.array) { + return nil + } + if offset < 0 { + offset = len(a.array) + offset + if offset < 0 { + return nil + } + } + if size < 0 { + offset += size + size = -size + if offset < 0 { + return nil + } + } + end := offset + size + if end > len(a.array) { + end = len(a.array) + size = len(a.array) - offset + } + if a.mu.IsSafe() { + s := make([]int, size) + copy(s, a.array[offset:]) + return s + } else { + return a.array[offset:end] + } +} + // See PushRight. func (a *IntArray) Append(value ...int) *IntArray { a.mu.Lock() @@ -488,27 +541,6 @@ func (a *IntArray) Pad(size int, value int) *IntArray { return a } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *IntArray) SubSlice(offset, size int) []int { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > len(a.array) { - size = len(a.array) - offset - } - if a.mu.IsSafe() { - s := make([]int, size) - copy(s, a.array[offset:]) - return s - } else { - return a.array[offset:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *IntArray) Rand() int { a.mu.RLock() diff --git a/g/container/garray/garray_normal_interface.go b/g/container/garray/garray_normal_interface.go index e95048ef2..c91cd2d97 100644 --- a/g/container/garray/garray_normal_interface.go +++ b/g/container/garray/garray_normal_interface.go @@ -9,11 +9,12 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" ) type Array struct { @@ -262,31 +263,83 @@ func (a *Array) PopRights(size int) []interface{} { // Range picks and returns items by range, like array[start:end]. // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. -func (a *Array) Range(start, end int) []interface{} { +// +// If is negative, then the offset will start from the end of array. +// If is omitted, then the sequence will have everything from start up +// until the end of the array. +func (a *Array) Range(start int, end ...int) []interface{} { a.mu.RLock() defer a.mu.RUnlock() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]interface{})(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]interface{}, end-start) - copy(array, a.array[start:end]) + array = make([]interface{}, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and parameters. +// If in concurrent safe usage, it returns a copy of the slice; else a pointer. +// +// If offset is non-negative, the sequence will start at that offset in the array. +// If offset is negative, the sequence will start that far from the end of the array. +// +// If length is given and is positive, then the sequence will have up to that many elements in it. +// If the array is shorter than the length, then only the available array elements will be present. +// If length is given and is negative then the sequence will stop that many elements from the end of the array. +// If it is omitted, then the sequence will have everything from offset up until the end of the array. +// +// Any possibility crossing the left border of array, it will fail. +func (a *Array) SubSlice(offset int, length ...int) []interface{} { + a.mu.RLock() + defer a.mu.RUnlock() + size := len(a.array) + if len(length) > 0 { + size = length[0] + } + if offset > len(a.array) { + return nil + } + if offset < 0 { + offset = len(a.array) + offset + if offset < 0 { + return nil + } + } + if size < 0 { + offset += size + size = -size + if offset < 0 { + return nil + } + } + end := offset + size + if end > len(a.array) { + end = len(a.array) + size = len(a.array) - offset + } + if a.mu.IsSafe() { + s := make([]interface{}, size) + copy(s, a.array[offset:]) + return s + } else { + return a.array[offset:end] + } +} + // See PushRight. func (a *Array) Append(value ...interface{}) *Array { a.PushRight(value...) @@ -482,27 +535,6 @@ func (a *Array) Pad(size int, val interface{}) *Array { return a } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *Array) SubSlice(offset, size int) []interface{} { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > len(a.array) { - size = len(a.array) - offset - } - if a.mu.IsSafe() { - s := make([]interface{}, size) - copy(s, a.array[offset:]) - return s - } else { - return a.array[offset:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *Array) Rand() interface{} { a.mu.RLock() diff --git a/g/container/garray/garray_normal_string.go b/g/container/garray/garray_normal_string.go index 702ecda52..bfc61092e 100644 --- a/g/container/garray/garray_normal_string.go +++ b/g/container/garray/garray_normal_string.go @@ -9,12 +9,13 @@ package garray import ( "bytes" "fmt" - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/util/gconv" - "github.com/gogf/gf/g/util/grand" "math" "sort" "strings" + + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/util/gconv" + "github.com/gogf/gf/g/util/grand" ) type StringArray struct { @@ -267,31 +268,83 @@ func (a *StringArray) PopRights(size int) []string { // Range picks and returns items by range, like array[start:end]. // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. -func (a *StringArray) Range(start, end int) []string { +// +// If is negative, then the offset will start from the end of array. +// If is omitted, then the sequence will have everything from start up +// until the end of the array. +func (a *StringArray) Range(start int, end ...int) []string { a.mu.RLock() defer a.mu.RUnlock() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]string)(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]string, end-start) - copy(array, a.array[start:end]) + array = make([]string, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and parameters. +// If in concurrent safe usage, it returns a copy of the slice; else a pointer. +// +// If offset is non-negative, the sequence will start at that offset in the array. +// If offset is negative, the sequence will start that far from the end of the array. +// +// If length is given and is positive, then the sequence will have up to that many elements in it. +// If the array is shorter than the length, then only the available array elements will be present. +// If length is given and is negative then the sequence will stop that many elements from the end of the array. +// If it is omitted, then the sequence will have everything from offset up until the end of the array. +// +// Any possibility crossing the left border of array, it will fail. +func (a *StringArray) SubSlice(offset int, length ...int) []string { + a.mu.RLock() + defer a.mu.RUnlock() + size := len(a.array) + if len(length) > 0 { + size = length[0] + } + if offset > len(a.array) { + return nil + } + if offset < 0 { + offset = len(a.array) + offset + if offset < 0 { + return nil + } + } + if size < 0 { + offset += size + size = -size + if offset < 0 { + return nil + } + } + end := offset + size + if end > len(a.array) { + end = len(a.array) + size = len(a.array) - offset + } + if a.mu.IsSafe() { + s := make([]string, size) + copy(s, a.array[offset:]) + return s + } else { + return a.array[offset:end] + } +} + // See PushRight. func (a *StringArray) Append(value ...string) *StringArray { a.mu.Lock() @@ -488,27 +541,6 @@ func (a *StringArray) Pad(size int, value string) *StringArray { return a } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *StringArray) SubSlice(offset, size int) []string { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > len(a.array) { - size = len(a.array) - offset - } - if a.mu.IsSafe() { - s := make([]string, size) - copy(s, a.array[offset:]) - return s - } else { - return a.array[offset:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *StringArray) Rand() string { a.mu.RLock() diff --git a/g/container/garray/garray_sorted_int.go b/g/container/garray/garray_sorted_int.go index e7f792479..8fcd9a8e7 100644 --- a/g/container/garray/garray_sorted_int.go +++ b/g/container/garray/garray_sorted_int.go @@ -9,12 +9,13 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" ) // It's using increasing order in default. @@ -216,31 +217,83 @@ func (a *SortedIntArray) PopRights(size int) []int { // Range picks and returns items by range, like array[start:end]. // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. -func (a *SortedIntArray) Range(start, end int) []int { +// +// If is negative, then the offset will start from the end of array. +// If is omitted, then the sequence will have everything from start up +// until the end of the array. +func (a *SortedIntArray) Range(start int, end ...int) []int { a.mu.RLock() defer a.mu.RUnlock() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]int)(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]int, end-start) - copy(array, a.array[start:end]) + array = make([]int, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and parameters. +// If in concurrent safe usage, it returns a copy of the slice; else a pointer. +// +// If offset is non-negative, the sequence will start at that offset in the array. +// If offset is negative, the sequence will start that far from the end of the array. +// +// If length is given and is positive, then the sequence will have up to that many elements in it. +// If the array is shorter than the length, then only the available array elements will be present. +// If length is given and is negative then the sequence will stop that many elements from the end of the array. +// If it is omitted, then the sequence will have everything from offset up until the end of the array. +// +// Any possibility crossing the left border of array, it will fail. +func (a *SortedIntArray) SubSlice(offset int, length ...int) []int { + a.mu.RLock() + defer a.mu.RUnlock() + size := len(a.array) + if len(length) > 0 { + size = length[0] + } + if offset > len(a.array) { + return nil + } + if offset < 0 { + offset = len(a.array) + offset + if offset < 0 { + return nil + } + } + if size < 0 { + offset += size + size = -size + if offset < 0 { + return nil + } + } + end := offset + size + if end > len(a.array) { + end = len(a.array) + size = len(a.array) - offset + } + if a.mu.IsSafe() { + s := make([]int, size) + copy(s, a.array[offset:]) + return s + } else { + return a.array[offset:end] + } +} + // Len returns the length of array. func (a *SortedIntArray) Len() int { a.mu.RLock() @@ -433,27 +486,6 @@ func (a *SortedIntArray) Chunk(size int) [][]int { return n } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *SortedIntArray) SubSlice(offset, size int) []int { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > len(a.array) { - size = len(a.array) - offset - } - if a.mu.IsSafe() { - s := make([]int, size) - copy(s, a.array[offset:]) - return s - } else { - return a.array[offset:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *SortedIntArray) Rand() int { a.mu.RLock() diff --git a/g/container/garray/garray_sorted_interface.go b/g/container/garray/garray_sorted_interface.go index cb18f5933..ea2966fae 100644 --- a/g/container/garray/garray_sorted_interface.go +++ b/g/container/garray/garray_sorted_interface.go @@ -9,12 +9,13 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" ) // It's using increasing order in default. @@ -217,31 +218,83 @@ func (a *SortedArray) PopRights(size int) []interface{} { // Range picks and returns items by range, like array[start:end]. // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. -func (a *SortedArray) Range(start, end int) []interface{} { +// +// If is negative, then the offset will start from the end of array. +// If is omitted, then the sequence will have everything from start up +// until the end of the array. +func (a *SortedArray) Range(start int, end ...int) []interface{} { a.mu.RLock() defer a.mu.RUnlock() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]interface{})(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]interface{}, end-start) - copy(array, a.array[start:end]) + array = make([]interface{}, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and parameters. +// If in concurrent safe usage, it returns a copy of the slice; else a pointer. +// +// If offset is non-negative, the sequence will start at that offset in the array. +// If offset is negative, the sequence will start that far from the end of the array. +// +// If length is given and is positive, then the sequence will have up to that many elements in it. +// If the array is shorter than the length, then only the available array elements will be present. +// If length is given and is negative then the sequence will stop that many elements from the end of the array. +// If it is omitted, then the sequence will have everything from offset up until the end of the array. +// +// Any possibility crossing the left border of array, it will fail. +func (a *SortedArray) SubSlice(offset int, length ...int) []interface{} { + a.mu.RLock() + defer a.mu.RUnlock() + size := len(a.array) + if len(length) > 0 { + size = length[0] + } + if offset > len(a.array) { + return nil + } + if offset < 0 { + offset = len(a.array) + offset + if offset < 0 { + return nil + } + } + if size < 0 { + offset += size + size = -size + if offset < 0 { + return nil + } + } + end := offset + size + if end > len(a.array) { + end = len(a.array) + size = len(a.array) - offset + } + if a.mu.IsSafe() { + s := make([]interface{}, size) + copy(s, a.array[offset:]) + return s + } else { + return a.array[offset:end] + } +} + // Sum returns the sum of values in an array. func (a *SortedArray) Sum() (sum int) { a.mu.RLock() @@ -434,27 +487,6 @@ func (a *SortedArray) Chunk(size int) [][]interface{} { return n } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *SortedArray) SubSlice(offset, size int) []interface{} { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > len(a.array) { - size = len(a.array) - offset - } - if a.mu.IsSafe() { - s := make([]interface{}, size) - copy(s, a.array[offset:]) - return s - } else { - return a.array[offset:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *SortedArray) Rand() interface{} { a.mu.RLock() diff --git a/g/container/garray/garray_sorted_string.go b/g/container/garray/garray_sorted_string.go index 2092d4f18..89e06b43d 100644 --- a/g/container/garray/garray_sorted_string.go +++ b/g/container/garray/garray_sorted_string.go @@ -9,13 +9,14 @@ package garray import ( "bytes" "fmt" + "math" + "sort" + "strings" + "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/internal/rwmutex" "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/g/util/grand" - "math" - "sort" - "strings" ) // It's using increasing order in default. @@ -211,31 +212,83 @@ func (a *SortedStringArray) PopRights(size int) []string { // Range picks and returns items by range, like array[start:end]. // Notice, if in concurrent-safe usage, it returns a copy of slice; // else a pointer to the underlying data. -func (a *SortedStringArray) Range(start, end int) []string { +// +// If is negative, then the offset will start from the end of array. +// If is omitted, then the sequence will have everything from start up +// until the end of the array. +func (a *SortedStringArray) Range(start int, end ...int) []string { a.mu.RLock() defer a.mu.RUnlock() - length := len(a.array) - if start > length || start > end { + offsetEnd := len(a.array) + if len(end) > 0 && end[0] < offsetEnd { + offsetEnd = end[0] + } + if start > offsetEnd { return nil } if start < 0 { start = 0 } - if end > length { - end = length - } array := ([]string)(nil) if a.mu.IsSafe() { - a.mu.RLock() - defer a.mu.RUnlock() - array = make([]string, end-start) - copy(array, a.array[start:end]) + array = make([]string, offsetEnd-start) + copy(array, a.array[start:offsetEnd]) } else { - array = a.array[start:end] + array = a.array[start:offsetEnd] } return array } +// SubSlice returns a slice of elements from the array as specified +// by the and parameters. +// If in concurrent safe usage, it returns a copy of the slice; else a pointer. +// +// If offset is non-negative, the sequence will start at that offset in the array. +// If offset is negative, the sequence will start that far from the end of the array. +// +// If length is given and is positive, then the sequence will have up to that many elements in it. +// If the array is shorter than the length, then only the available array elements will be present. +// If length is given and is negative then the sequence will stop that many elements from the end of the array. +// If it is omitted, then the sequence will have everything from offset up until the end of the array. +// +// Any possibility crossing the left border of array, it will fail. +func (a *SortedStringArray) SubSlice(offset int, length ...int) []string { + a.mu.RLock() + defer a.mu.RUnlock() + size := len(a.array) + if len(length) > 0 { + size = length[0] + } + if offset > len(a.array) { + return nil + } + if offset < 0 { + offset = len(a.array) + offset + if offset < 0 { + return nil + } + } + if size < 0 { + offset += size + size = -size + if offset < 0 { + return nil + } + } + end := offset + size + if end > len(a.array) { + end = len(a.array) + size = len(a.array) - offset + } + if a.mu.IsSafe() { + s := make([]string, size) + copy(s, a.array[offset:]) + return s + } else { + return a.array[offset:end] + } +} + // Sum returns the sum of values in an array. func (a *SortedStringArray) Sum() (sum int) { a.mu.RLock() @@ -428,27 +481,6 @@ func (a *SortedStringArray) Chunk(size int) [][]string { return n } -// SubSlice returns a slice of elements from the array as specified -// by the and parameters. -// If in concurrent safe usage, it returns a copy of the slice; else a pointer. -func (a *SortedStringArray) SubSlice(offset, size int) []string { - a.mu.RLock() - defer a.mu.RUnlock() - if offset > len(a.array) { - return nil - } - if offset+size > len(a.array) { - size = len(a.array) - offset - } - if a.mu.IsSafe() { - s := make([]string, size) - copy(s, a.array[offset:]) - return s - } else { - return a.array[offset:] - } -} - // Rand randomly returns one item from array(no deleting). func (a *SortedStringArray) Rand() string { a.mu.RLock() diff --git a/g/container/garray/garray_z_unit_int_test.go b/g/container/garray/garray_z_unit_int_test.go index 6d7e53377..2a65f89f3 100644 --- a/g/container/garray/garray_z_unit_int_test.go +++ b/g/container/garray/garray_z_unit_int_test.go @@ -9,9 +9,10 @@ package garray_test import ( + "testing" + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/test/gtest" - "testing" ) func Test_IntArray_Basic(t *testing.T) { @@ -100,6 +101,7 @@ func TestIntArray_Range(t *testing.T) { gtest.Assert(array1.Range(0, 1), []int{0}) gtest.Assert(array1.Range(1, 2), []int{1}) gtest.Assert(array1.Range(0, 2), []int{0, 1}) + gtest.Assert(array1.Range(10, 2), nil) gtest.Assert(array1.Range(-1, 10), value1) }) } @@ -151,9 +153,21 @@ func TestIntArray_SubSlice(t *testing.T) { gtest.Case(t, func() { a1 := []int{0, 1, 2, 3, 4, 5, 6} array1 := garray.NewIntArrayFrom(a1) + gtest.Assert(array1.SubSlice(6), []int{6}) + gtest.Assert(array1.SubSlice(5), []int{5, 6}) + gtest.Assert(array1.SubSlice(8), nil) gtest.Assert(array1.SubSlice(0, 2), []int{0, 1}) gtest.Assert(array1.SubSlice(2, 2), []int{2, 3}) gtest.Assert(array1.SubSlice(5, 8), []int{5, 6}) + gtest.Assert(array1.SubSlice(-1, 1), []int{6}) + gtest.Assert(array1.SubSlice(-1, 9), []int{6}) + gtest.Assert(array1.SubSlice(-2, 3), []int{5, 6}) + gtest.Assert(array1.SubSlice(-7, 3), []int{0, 1, 2}) + gtest.Assert(array1.SubSlice(-8, 3), nil) + gtest.Assert(array1.SubSlice(-1, -3), []int{3, 4, 5}) + gtest.Assert(array1.SubSlice(-9, 3), nil) + gtest.Assert(array1.SubSlice(1, -1), []int{0}) + gtest.Assert(array1.SubSlice(1, -3), nil) }) } @@ -435,7 +449,6 @@ func TestSortedIntArray_Chunk(t *testing.T) { array1 := garray.NewSortedIntArrayFrom(a1) ns1 := array1.Chunk(2) //按每几个元素切成一个数组 ns2 := array1.Chunk(-1) - t.Log(ns1) gtest.Assert(len(ns1), 3) gtest.Assert(ns1[0], []int{1, 2}) gtest.Assert(ns1[2], []int{5}) diff --git a/g/container/gpool/gpool.go b/g/container/gpool/gpool.go index 3f9ccdd5e..508105183 100644 --- a/g/container/gpool/gpool.go +++ b/g/container/gpool/gpool.go @@ -9,11 +9,12 @@ package gpool import ( "errors" + "time" + "github.com/gogf/gf/g/container/glist" "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/os/gtimer" - "time" ) // Object-Reusable Pool. @@ -126,6 +127,7 @@ func (p *Pool) checkExpire() { gtimer.Exit() } for { + // TODO Do not use Pop and Push mechanism, which is not graceful. if r := p.list.PopFront(); r != nil { item := r.(*poolItem) if item.expire == 0 || item.expire > gtime.Millisecond() { diff --git a/g/container/gtype/int64.go b/g/container/gtype/int64.go index ee1846c1b..6c961efb1 100644 --- a/g/container/gtype/int64.go +++ b/g/container/gtype/int64.go @@ -41,7 +41,7 @@ func (v *Int64) Val() int64 { } // Add atomically adds to t.value and returns the new value. -func (v *Int64) Add(delta int64) int64 { +func (v *Int64) Add(delta int64) (new int64) { return atomic.AddInt64(&v.value, delta) } diff --git a/g/crypto/gcrc32/gcrc32_test.go b/g/crypto/gcrc32/gcrc32_test.go index 753e1c034..f1977d098 100644 --- a/g/crypto/gcrc32/gcrc32_test.go +++ b/g/crypto/gcrc32/gcrc32_test.go @@ -14,6 +14,7 @@ import ( "github.com/gogf/gf/g/crypto/gmd5" "github.com/gogf/gf/g/crypto/gcrc32" + "github.com/gogf/gf/g/crypto/gmd5" "github.com/gogf/gf/g/test/gtest" ) diff --git a/g/crypto/gmd5/gmd5.go b/g/crypto/gmd5/gmd5.go index a4829e97a..34893d7ff 100644 --- a/g/crypto/gmd5/gmd5.go +++ b/g/crypto/gmd5/gmd5.go @@ -11,10 +11,10 @@ import ( "crypto/md5" "errors" "fmt" + "github.com/gogf/gf/g/internal/errors" + "github.com/gogf/gf/g/util/gconv" "io" "os" - - "github.com/gogf/gf/g/util/gconv" ) // Encrypt encrypts any type of variable using MD5 algorithms. @@ -40,9 +40,7 @@ func EncryptFile(path string) (encrypt string, err error) { return "", err } defer func() { - if e := f.Close(); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(f.Close(), "file closing error") }() h := md5.New() _, err = io.Copy(h, f) diff --git a/g/crypto/gsha1/gsha1.go b/g/crypto/gsha1/gsha1.go index b296bdb74..7ab16c27a 100644 --- a/g/crypto/gsha1/gsha1.go +++ b/g/crypto/gsha1/gsha1.go @@ -10,11 +10,10 @@ package gsha1 import ( "crypto/sha1" "encoding/hex" - "errors" + "github.com/gogf/gf/g/internal/errors" + "github.com/gogf/gf/g/util/gconv" "io" "os" - - "github.com/gogf/gf/g/util/gconv" ) // Encrypt encrypts any type of variable using SHA1 algorithms. @@ -37,9 +36,7 @@ func EncryptFile(path string) (encrypt string, err error) { return "", err } defer func() { - if e := f.Close(); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(f.Close(), "file closing error") }() h := sha1.New() _, err = io.Copy(h, f) diff --git a/g/database/gdb/gdb_unit_basic_test.go b/g/database/gdb/gdb_unit_basic_test.go index 4506bd8ef..3a28160f8 100644 --- a/g/database/gdb/gdb_unit_basic_test.go +++ b/g/database/gdb/gdb_unit_basic_test.go @@ -7,9 +7,10 @@ package gdb_test import ( + "testing" + "github.com/gogf/gf/g/database/gdb" "github.com/gogf/gf/g/test/gtest" - "testing" ) func Test_Instance(t *testing.T) { diff --git a/g/encoding/gjson/gjson.go b/g/encoding/gjson/gjson.go index 755a658f4..4af8249ee 100644 --- a/g/encoding/gjson/gjson.go +++ b/g/encoding/gjson/gjson.go @@ -8,12 +8,13 @@ package gjson import ( - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/text/gstr" - "github.com/gogf/gf/g/util/gconv" "reflect" "strconv" "strings" + + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/text/gstr" + "github.com/gogf/gf/g/util/gconv" ) const ( @@ -26,8 +27,14 @@ type Json struct { mu *rwmutex.RWMutex p *interface{} // Pointer for hierarchical data access, it's the root of data in default. c byte // Char separator('.' in default). - vc bool // Violence Check(false in default), which is used to access data + // Violence Check(false in default), which is used to access data // when the hierarchical data key contains separator char. + vc bool +} + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (j *Json) MarshalJSON() ([]byte, error) { + return j.ToJson() } // Set by . diff --git a/g/encoding/gparser/gparser_api_encoding.go b/g/encoding/gparser/gparser_api_encoding.go index d1208b2c4..efc342731 100644 --- a/g/encoding/gparser/gparser_api_encoding.go +++ b/g/encoding/gparser/gparser_api_encoding.go @@ -6,6 +6,11 @@ package gparser +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (p *Parser) MarshalJSON() ([]byte, error) { + return p.json.MarshalJSON() +} + func (p *Parser) ToXml(rootTag ...string) ([]byte, error) { return p.json.ToXml(rootTag...) } diff --git a/g/frame/gins/gins_view_test.go b/g/frame/gins/gins_view_test.go index 9b3598d35..249b052f5 100644 --- a/g/frame/gins/gins_view_test.go +++ b/g/frame/gins/gins_view_test.go @@ -8,11 +8,12 @@ package gins_test import ( "fmt" + "testing" + "github.com/gogf/gf/g/frame/gins" "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/test/gtest" - "testing" ) func Test_View(t *testing.T) { diff --git a/g/g.go b/g/g.go index 23f6bccb3..8a1350e59 100644 --- a/g/g.go +++ b/g/g.go @@ -39,6 +39,8 @@ type Slice = []interface{} type SliceAny = []interface{} type SliceStr = []string type SliceInt = []int + +// Array is alias of Slice. type Array = []interface{} type ArrayAny = []interface{} type ArrayStr = []string diff --git a/g/g_func.go b/g/g_func.go index 1d6198d6f..ea5ac080f 100644 --- a/g/g_func.go +++ b/g/g_func.go @@ -39,7 +39,7 @@ func Throw(exception interface{}) { gutil.Throw(exception) } -// TryCatch does the try...catch... logic. +// TryCatch does the try...catch... mechanism. func TryCatch(try func(), catch ...func(exception interface{})) { gutil.TryCatch(try, catch...) } diff --git a/g/g_logger.go b/g/g_logger.go index f64589a03..41b2556dd 100644 --- a/g/g_logger.go +++ b/g/g_logger.go @@ -10,7 +10,7 @@ import ( "github.com/gogf/gf/g/os/glog" ) -// SetDebug disables/enables debug level for logging globally. +// SetDebug disables/enables debug level for logging component globally. func SetDebug(debug bool) { glog.SetDebug(debug) } diff --git a/g/g_object.go b/g/g_object.go index f83aea35e..9d3f7f64d 100644 --- a/g/g_object.go +++ b/g/g_object.go @@ -47,7 +47,8 @@ func Database(name ...string) gdb.DB { return gins.Database(name...) } -// DB is alias of Database. See Database. +// DB is alias of Database. +// See Database. func DB(name ...string) gdb.DB { return gins.Database(name...) } diff --git a/g/g_setting.go b/g/g_setting.go index f220806ca..a19187859 100644 --- a/g/g_setting.go +++ b/g/g_setting.go @@ -8,7 +8,7 @@ package g import "github.com/gogf/gf/g/net/ghttp" -// SetServerGraceful enables/disables graceful/hot reload feature of http Web Server. +// SetServerGraceful enables/disables graceful reload feature of http Web Server. // This feature is disabled in default. func SetServerGraceful(enabled bool) { ghttp.SetGraceful(enabled) diff --git a/g/internal/cmdenv/cmdenv_test.go b/g/internal/cmdenv/cmdenv_test.go index 0eb05a7aa..90702733c 100644 --- a/g/internal/cmdenv/cmdenv_test.go +++ b/g/internal/cmdenv/cmdenv_test.go @@ -9,9 +9,10 @@ package cmdenv import ( - "github.com/gogf/gf/g/test/gtest" "os" "testing" + + "github.com/gogf/gf/g/test/gtest" ) func Test_Get(t *testing.T) { diff --git a/g/internal/errors/errors.go b/g/internal/errors/errors.go new file mode 100644 index 000000000..7d6270869 --- /dev/null +++ b/g/internal/errors/errors.go @@ -0,0 +1,48 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package errors provides simple functions to manipulate errors. +// +// This package can be scalable due to https://go.googlesource.com/proposal/+/master/design/go2draft.md. +package errors + +import "github.com/gogf/gf/g/util/gconv" + +// errorWrapper is a simple wrapper for errors. +type errorWrapper struct { + s string +} + +// New returns an error that formats as the given value. +func New(value interface{}) error { + if value == nil { + return nil + } + return NewText(gconv.String(value)) +} + +// NewText returns an error that formats as the given text. +func NewText(text string) error { + if text == "" { + return nil + } + return &errorWrapper{ + s: text, + } +} + +// Wrap wraps error with text. +func Wrap(err error, text string) error { + if err == nil { + return nil + } + return NewText(text + ": " + err.Error()) +} + +// Error implements interface Error. +func (e *errorWrapper) Error() string { + return e.s +} diff --git a/g/internal/errors/errors_test.go b/g/internal/errors/errors_test.go new file mode 100644 index 000000000..7d0328baa --- /dev/null +++ b/g/internal/errors/errors_test.go @@ -0,0 +1,39 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package errors_test + +import ( + "testing" + + "github.com/gogf/gf/g/internal/errors" + "github.com/gogf/gf/g/test/gtest" +) + +func interfaceNil() interface{} { + return nil +} + +func nilError() error { + return nil +} + +func Test_Nil(t *testing.T) { + gtest.Case(t, func() { + gtest.Assert(errors.New(interfaceNil()), nil) + gtest.Assert(errors.Wrap(nilError(), "test"), nil) + }) +} + +func Test_Wrap(t *testing.T) { + gtest.Case(t, func() { + err := errors.New("1") + err = errors.Wrap(err, "func2 error") + err = errors.Wrap(err, "func3 error") + gtest.AssertNE(err, nil) + gtest.Assert(err.Error(), "func3 error: func2 error: 1") + }) +} diff --git a/g/net/ghttp/ghttp_response_cors.go b/g/net/ghttp/ghttp_response_cors.go index 708cfed8d..d5dcc9d36 100644 --- a/g/net/ghttp/ghttp_response_cors.go +++ b/g/net/ghttp/ghttp_response_cors.go @@ -8,7 +8,6 @@ package ghttp import ( - "github.com/gogf/gf/g/text/gstr" "github.com/gogf/gf/g/util/gconv" ) @@ -26,7 +25,7 @@ type CORSOptions struct { // 默认的CORS配置 func (r *Response) DefaultCORSOptions() CORSOptions { return CORSOptions{ - AllowOrigin: gstr.TrimRight(r.request.Referer(), "/"), + AllowOrigin: "*", AllowMethods: HTTP_METHODS, AllowCredentials: "true", MaxAge: 3628800, diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 98f58c6ca..1f2886ced 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -10,6 +10,14 @@ import ( "bytes" "errors" "fmt" + "net/http" + "os" + "reflect" + "runtime" + "strings" + "sync" + "time" + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gtype" @@ -23,13 +31,6 @@ import ( "github.com/gogf/gf/g/util/gconv" "github.com/gogf/gf/third/github.com/gorilla/websocket" "github.com/gogf/gf/third/github.com/olekukonko/tablewriter" - "net/http" - "os" - "reflect" - "runtime" - "strings" - "sync" - "time" ) type ( @@ -382,15 +383,14 @@ func (s *Server) GetRouteMap() string { } // 阻塞执行监听 -func (s *Server) Run() error { +func (s *Server) Run() { if err := s.Start(); err != nil { - return err + glog.Fatal(err) } // 阻塞等待服务执行完成 <-s.closeChan glog.Printf("%d: all servers shutdown", gproc.Pid()) - return nil } // 阻塞等待所有Web Server停止,常用于多Web Server场景,以及需要将Web Server异步运行的场景 diff --git a/g/net/gtcp/gtcp_conn.go b/g/net/gtcp/gtcp_conn.go index f1ace01e9..171642f35 100644 --- a/g/net/gtcp/gtcp_conn.go +++ b/g/net/gtcp/gtcp_conn.go @@ -14,6 +14,8 @@ import ( "io" "net" "time" + + "github.com/gogf/gf/g/internal/errors" ) // 封装的链接对象 @@ -208,9 +210,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.Recv(length, retry...) return @@ -222,9 +222,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.Send(data, retry...) return diff --git a/g/net/gtcp/gtcp_conn_pkg.go b/g/net/gtcp/gtcp_conn_pkg.go index 0bc114a50..90b02ca97 100644 --- a/g/net/gtcp/gtcp_conn_pkg.go +++ b/g/net/gtcp/gtcp_conn_pkg.go @@ -8,9 +8,10 @@ package gtcp import ( "encoding/binary" - "errors" "fmt" "time" + + "github.com/gogf/gf/g/internal/errors" ) const ( @@ -73,9 +74,7 @@ func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ... return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.SendPkg(data, option...) return @@ -148,9 +147,7 @@ func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (d return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.RecvPkg(option...) return diff --git a/g/net/gtcp/gtcp_pool.go b/g/net/gtcp/gtcp_pool.go index ee65f0f61..1f3ceaaf1 100644 --- a/g/net/gtcp/gtcp_pool.go +++ b/g/net/gtcp/gtcp_pool.go @@ -7,11 +7,10 @@ package gtcp import ( - "errors" - "time" - "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gpool" + "github.com/gogf/gf/g/internal/errors" + "time" ) // 链接池链接对象 @@ -121,9 +120,7 @@ func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...R return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.Recv(length, retry...) return @@ -135,9 +132,7 @@ func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ... return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.Send(data, retry...) return diff --git a/g/net/gtcp/gtcp_pool_pkg.go b/g/net/gtcp/gtcp_pool_pkg.go index 6b71eea0d..861a4967e 100644 --- a/g/net/gtcp/gtcp_pool_pkg.go +++ b/g/net/gtcp/gtcp_pool_pkg.go @@ -9,6 +9,8 @@ package gtcp import ( "errors" "time" + + "github.com/gogf/gf/g/internal/errors" ) // 简单协议: (方法覆盖)发送数据 @@ -46,9 +48,7 @@ func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.RecvPkg(option...) return @@ -60,9 +60,7 @@ func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.SendPkg(data, option...) return diff --git a/g/net/gudp/gudp_conn.go b/g/net/gudp/gudp_conn.go index 1d59791f9..119d8ec00 100644 --- a/g/net/gudp/gudp_conn.go +++ b/g/net/gudp/gudp_conn.go @@ -11,6 +11,8 @@ import ( "io" "net" "time" + + "github.com/gogf/gf/g/internal/errors" ) // 封装的UDP链接对象 @@ -181,9 +183,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry return nil, err } defer func() { - if e := c.SetRecvDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error") }() data, err = c.Recv(length, retry...) return @@ -195,9 +195,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr return err } defer func() { - if e := c.SetSendDeadline(time.Time{}); e != nil { - err = errors.New(err.Error() + "; " + e.Error()) - } + err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error") }() err = c.Send(data, retry...) return diff --git a/g/os/gcron/gcron_unit_1_test.go b/g/os/gcron/gcron_unit_1_test.go index 59e3062ee..125fc2edc 100644 --- a/g/os/gcron/gcron_unit_1_test.go +++ b/g/os/gcron/gcron_unit_1_test.go @@ -7,11 +7,12 @@ package gcron_test import ( + "testing" + "time" + "github.com/gogf/gf/g/container/garray" "github.com/gogf/gf/g/os/gcron" "github.com/gogf/gf/g/test/gtest" - "testing" - "time" ) func TestCron_Add_Close(t *testing.T) { @@ -146,7 +147,7 @@ func TestCron_AddOnce2(t *testing.T) { array.Append(1) }) gtest.Assert(cron.Size(), 1) - time.Sleep(2500 * time.Millisecond) + time.Sleep(3000 * time.Millisecond) gtest.Assert(array.Len(), 1) gtest.Assert(cron.Size(), 0) }) diff --git a/g/os/gflock/gflock.go b/g/os/gflock/gflock.go index 9bcc0d6e9..e629720ca 100644 --- a/g/os/gflock/gflock.go +++ b/g/os/gflock/gflock.go @@ -10,13 +10,11 @@ package gflock import ( "github.com/gogf/gf/g/os/gfile" "github.com/gogf/gf/third/github.com/theckman/go-flock" - "sync" ) // File locker. type Locker struct { - mu sync.RWMutex // 用于外部接口调用的互斥锁(阻塞机制) - flock *flock.Flock // 底层文件锁对象 + flock *flock.Flock // Underlying file locker. } // New creates and returns a new file locker with given . @@ -47,9 +45,6 @@ func (l *Locker) IsLocked() bool { // It returns true if success, or else returns false immediately. func (l *Locker) TryLock() bool { ok, _ := l.flock.TryLock() - if ok { - l.mu.Lock() - } return ok } @@ -57,32 +52,61 @@ func (l *Locker) TryLock() bool { // It returns true if success, or else returns false immediately. func (l *Locker) TryRLock() bool { ok, _ := l.flock.TryRLock() - if ok { - l.mu.RLock() - } return ok } +// Lock is a blocking call to try and take an exclusive file lock. It will wait +// until it is able to obtain the exclusive file lock. It's recommended that +// TryLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already exclusive-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. +// +// If the *Flock has a shared lock (RLock), this may transparently replace the +// shared lock with an exclusive lock on some UNIX-like operating systems. Be +// careful when using exclusive locks in conjunction with shared locks +// (RLock()), because calling Unlock() may accidentally release the exclusive +// lock that was once a shared lock. func (l *Locker) Lock() (err error) { - l.mu.Lock() - err = l.flock.Lock() - return + return l.flock.Lock() } +// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so +// while it is running the Locked() and RLocked() functions will be blocked. +// +// This function short-circuits if we are unlocked already. If not, it calls +// syscall.LOCK_UN on the file and closes the file descriptor. It does not +// remove the file from disk. It's up to your application to do. +// +// Please note, if your shared lock became an exclusive lock this may +// unintentionally drop the exclusive lock if called by the consumer that +// believes they have a shared lock. Please see Lock() for more details. func (l *Locker) UnLock() (err error) { - err = l.flock.Unlock() - l.mu.Unlock() - return + return l.flock.Unlock() } +// RLock is a blocking call to try and take a ahred file lock. It will wait +// until it is able to obtain the shared file lock. It's recommended that +// TryRLock() be used over this function. This function may block the ability to +// query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already shared-locked, this function short-circuits and returns +// immediately assuming it can take the mutex lock. func (l *Locker) RLock() (err error) { - l.mu.RLock() - err = l.flock.RLock() - return + return l.flock.RLock() } +// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so +// while it is running the Locked() and RLocked() functions will be blocked. +// +// This function short-circuits if we are unlocked already. If not, it calls +// syscall.LOCK_UN on the file and closes the file descriptor. It does not +// remove the file from disk. It's up to your application to do. +// +// Please note, if your shared lock became an exclusive lock this may +// unintentionally drop the exclusive lock if called by the consumer that +// believes they have a shared lock. Please see Lock() for more details. func (l *Locker) RUnlock() (err error) { - err = l.flock.Unlock() - l.mu.RUnlock() - return + return l.flock.Unlock() } diff --git a/g/os/gmlock/gmlock.go b/g/os/gmlock/gmlock.go index 7d61a7fb5..a6baa38ee 100644 --- a/g/os/gmlock/gmlock.go +++ b/g/os/gmlock/gmlock.go @@ -7,26 +7,23 @@ // Package gmlock implements a concurrent-safe memory-based locker. package gmlock -import "time" - var ( // Default locker. locker = New() ) -// TryLock tries locking the with writing lock, -// it returns true if success, or if there's a write/reading lock the , -// it returns false. The parameter specifies the max duration it locks. -func TryLock(key string, expire ...time.Duration) bool { - return locker.TryLock(key, expire...) -} - // Lock locks the with writing lock. // If there's a write/reading lock the , // it will blocks until the lock is released. -// The parameter specifies the max duration it locks. -func Lock(key string, expire ...time.Duration) { - locker.Lock(key, expire...) +func Lock(key string) { + locker.Lock(key) +} + +// TryLock tries locking the with writing lock, +// it returns true if success, or if there's a write/reading lock the , +// it returns false. +func TryLock(key string) bool { + return locker.TryLock(key) } // Unlock unlocks the writing lock of the . @@ -34,12 +31,6 @@ func Unlock(key string) { locker.Unlock(key) } -// TryRLock tries locking the with reading lock. -// It returns true if success, or if there's a writing lock on , it returns false. -func TryRLock(key string) bool { - return locker.TryRLock(key) -} - // RLock locks the with reading lock. // If there's a writing lock on , // it will blocks until the writing lock is released. @@ -47,40 +38,24 @@ func RLock(key string) { locker.RLock(key) } +// TryRLock tries locking the with reading lock. +// It returns true if success, or if there's a writing lock on , it returns false. +func TryRLock(key string) bool { + return locker.TryRLock(key) +} + // RUnlock unlocks the reading lock of the . func RUnlock(key string) { locker.RUnlock(key) } -// TryLockFunc locks the with writing lock and callback function . -// It returns true if success, or else if there's a write/reading lock the , it return false. -// -// It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func TryLockFunc(key string, f func(), expire ...time.Duration) bool { - return locker.TryLockFunc(key, f, expire...) -} - -// TryRLockFunc locks the with reading lock and callback function . -// It returns true if success, or else if there's a writing lock the , it returns false. -// -// It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func TryRLockFunc(key string, f func()) bool { - return locker.TryRLockFunc(key, f) -} - // LockFunc locks the with writing lock and callback function . // If there's a write/reading lock the , // it will blocks until the lock is released. // // It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func LockFunc(key string, f func(), expire ...time.Duration) { - locker.LockFunc(key, f, expire...) +func LockFunc(key string, f func()) { + locker.LockFunc(key, f) } // RLockFunc locks the with reading lock and callback function . @@ -88,8 +63,27 @@ func LockFunc(key string, f func(), expire ...time.Duration) { // it will blocks until the lock is released. // // It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. func RLockFunc(key string, f func()) { locker.RLockFunc(key, f) } + +// TryLockFunc locks the with writing lock and callback function . +// It returns true if success, or else if there's a write/reading lock the , it return false. +// +// It releases the lock after is executed. +func TryLockFunc(key string, f func()) bool { + return locker.TryLockFunc(key, f) +} + +// TryRLockFunc locks the with reading lock and callback function . +// It returns true if success, or else if there's a writing lock the , it returns false. +// +// It releases the lock after is executed. +func TryRLockFunc(key string, f func()) bool { + return locker.TryRLockFunc(key, f) +} + +// Remove removes mutex with given . +func Remove(key string) { + locker.Remove(key) +} diff --git a/g/os/gmlock/gmlock_locker.go b/g/os/gmlock/gmlock_locker.go index d341e1a97..ce00c132d 100644 --- a/g/os/gmlock/gmlock_locker.go +++ b/g/os/gmlock/gmlock_locker.go @@ -8,11 +8,12 @@ package gmlock import ( "github.com/gogf/gf/g/container/gmap" - "github.com/gogf/gf/g/os/gtimer" - "time" + "github.com/gogf/gf/g/os/gmutex" ) // Memory locker. +// Note that there's no cache expire mechanism for mutex in locker. +// You need remove certain mutex manually when you do not want use it any more. type Locker struct { m *gmap.StrAnyMap } @@ -25,56 +26,74 @@ func New() *Locker { } } -// TryLock tries locking the with writing lock, -// it returns true if success, or it returns false if there's a writing/reading lock the . -// The parameter specifies the max duration it locks. -func (l *Locker) TryLock(key string, expire ...time.Duration) bool { - return l.doLock(key, l.getExpire(expire...), true) -} - // Lock locks the with writing lock. // If there's a write/reading lock the , // it will blocks until the lock is released. -// The parameter specifies the max duration it locks. -func (l *Locker) Lock(key string, expire ...time.Duration) { - l.doLock(key, l.getExpire(expire...), false) +func (l *Locker) Lock(key string) { + l.getOrNewMutex(key).Lock() +} + +// TryLock tries locking the with writing lock, +// it returns true if success, or it returns false if there's a writing/reading lock the . +func (l *Locker) TryLock(key string) bool { + return l.getOrNewMutex(key).TryLock() } // Unlock unlocks the writing lock of the . func (l *Locker) Unlock(key string) { if v := l.m.Get(key); v != nil { - v.(*Mutex).Unlock() + v.(*gmutex.Mutex).Unlock() } } -// TryRLock tries locking the with reading lock. -// It returns true if success, or if there's a writing lock on , it returns false. -func (l *Locker) TryRLock(key string) bool { - return l.doRLock(key, true) -} - // RLock locks the with reading lock. // If there's a writing lock on , // it will blocks until the writing lock is released. func (l *Locker) RLock(key string) { - l.doRLock(key, false) + l.getOrNewMutex(key).RLock() +} + +// TryRLock tries locking the with reading lock. +// It returns true if success, or if there's a writing lock on , it returns false. +func (l *Locker) TryRLock(key string) bool { + return l.getOrNewMutex(key).TryRLock() } // RUnlock unlocks the reading lock of the . func (l *Locker) RUnlock(key string) { if v := l.m.Get(key); v != nil { - v.(*Mutex).RUnlock() + v.(*gmutex.Mutex).RUnlock() } } +// LockFunc locks the with writing lock and callback function . +// If there's a write/reading lock the , +// it will blocks until the lock is released. +// +// It releases the lock after is executed. +func (l *Locker) LockFunc(key string, f func()) { + l.Lock(key) + defer l.Unlock(key) + f() +} + +// RLockFunc locks the with reading lock and callback function . +// If there's a writing lock the , +// it will blocks until the lock is released. +// +// It releases the lock after is executed. +func (l *Locker) RLockFunc(key string, f func()) { + l.RLock(key) + defer l.RUnlock(key) + f() +} + // TryLockFunc locks the with writing lock and callback function . // It returns true if success, or else if there's a write/reading lock the , it return false. // // It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func (l *Locker) TryLockFunc(key string, f func(), expire ...time.Duration) bool { - if l.TryLock(key, expire...) { +func (l *Locker) TryLockFunc(key string, f func()) bool { + if l.TryLock(key) { defer l.Unlock(key) f() return true @@ -86,8 +105,6 @@ func (l *Locker) TryLockFunc(key string, f func(), expire ...time.Duration) bool // It returns true if success, or else if there's a writing lock the , it returns false. // // It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. func (l *Locker) TryRLockFunc(key string, f func()) bool { if l.TryRLock(key) { defer l.RUnlock(key) @@ -97,90 +114,20 @@ func (l *Locker) TryRLockFunc(key string, f func()) bool { return false } -// LockFunc locks the with writing lock and callback function . -// If there's a write/reading lock the , -// it will blocks until the lock is released. -// -// It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func (l *Locker) LockFunc(key string, f func(), expire ...time.Duration) { - l.Lock(key, expire...) - defer l.Unlock(key) - f() +// Remove removes mutex with given from locker. +func (l *Locker) Remove(key string) { + l.m.Remove(key) } -// RLockFunc locks the with reading lock and callback function . -// If there's a writing lock the , -// it will blocks until the lock is released. -// -// It releases the lock after is executed. -// -// The parameter specifies the max duration it locks. -func (l *Locker) RLockFunc(key string, f func()) { - l.RLock(key) - defer l.RUnlock(key) - f() -} - -// getExpire returns the duration object passed. -// If is not passed, it returns a default duration object. -func (l *Locker) getExpire(expire ...time.Duration) time.Duration { - e := time.Duration(0) - if len(expire) > 0 { - e = expire[0] - } - return e -} - -// doLock locks writing on . -// It returns true if success, or else returns false. -// -// The parameter is true, -// it returns false immediately if it fails getting the writing lock. -// If is false, it blocks until it gets the writing lock. -// -// The parameter specifies the max duration it locks. -func (l *Locker) doLock(key string, expire time.Duration, try bool) bool { - mu := l.getOrNewMutex(key) - ok := true - if try { - ok = mu.TryLock() - } else { - mu.Lock() - } - if ok && expire > 0 { - wid := mu.wid.Val() - gtimer.AddOnce(expire, func() { - if wid == mu.wid.Val() { - mu.Unlock() - } - }) - } - return ok -} - -// doRLock locks reading on . -// It returns true if success, or else returns false. -// -// The parameter is true, -// it returns false immediately if it fails getting the reading lock. -// If is false, it blocks until it gets the reading lock. -func (l *Locker) doRLock(key string, try bool) bool { - mu := l.getOrNewMutex(key) - ok := true - if try { - ok = mu.TryRLock() - } else { - mu.RLock() - } - return ok +// Clear removes all mutexes from locker. +func (l *Locker) Clear() { + l.m.Clear() } // getOrNewMutex returns the mutex of given if it exists, // or else creates and returns a new one. -func (l *Locker) getOrNewMutex(key string) *Mutex { +func (l *Locker) getOrNewMutex(key string) *gmutex.Mutex { return l.m.GetOrSetFuncLock(key, func() interface{} { - return NewMutex() - }).(*Mutex) + return gmutex.New() + }).(*gmutex.Mutex) } diff --git a/g/os/gmlock/gmlock_unit_lock_test.go b/g/os/gmlock/gmlock_unit_lock_test.go index da7c4f7df..e62be3b7d 100644 --- a/g/os/gmlock/gmlock_unit_lock_test.go +++ b/g/os/gmlock/gmlock_unit_lock_test.go @@ -44,58 +44,61 @@ func Test_Locker_Lock(t *testing.T) { time.Sleep(50 * time.Millisecond) gtest.Assert(array.Len(), 4) }) - //expire +} + +func Test_Locker_LockFunc(t *testing.T) { + //no expire gtest.Case(t, func() { - key := "testLockExpire" + key := "testLockFunc" array := garray.New() go func() { - gmlock.Lock(key, 100*time.Millisecond) - array.Append(1) + gmlock.LockFunc(key, func() { + array.Append(1) + time.Sleep(50 * time.Millisecond) + }) // }() go func() { time.Sleep(10 * time.Millisecond) - gmlock.Lock(key) - time.Sleep(100 * time.Millisecond) - array.Append(1) - gmlock.Unlock(key) + gmlock.LockFunc(key, func() { + array.Append(1) + }) }() - time.Sleep(150 * time.Millisecond) + time.Sleep(10 * time.Millisecond) gtest.Assert(array.Len(), 1) - time.Sleep(250 * time.Millisecond) + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) // + time.Sleep(50 * time.Millisecond) gtest.Assert(array.Len(), 2) }) } -func Test_Locker_TryLock(t *testing.T) { +func Test_Locker_TryLockFunc(t *testing.T) { + //no expire gtest.Case(t, func() { - key := "testTryLock" + key := "testTryLockFunc" array := garray.New() go func() { - if gmlock.TryLock(key, 200*time.Millisecond) { + gmlock.TryLockFunc(key, func() { array.Append(1) - } + time.Sleep(50 * time.Millisecond) + }) }() go func() { - time.Sleep(100 * time.Millisecond) - if !gmlock.TryLock(key) { + time.Sleep(10 * time.Millisecond) + gmlock.TryLockFunc(key, func() { array.Append(1) - } else { - gmlock.Unlock(key) - } + }) }() go func() { - time.Sleep(300 * time.Millisecond) - if gmlock.TryLock(key) { + time.Sleep(70 * time.Millisecond) + gmlock.TryLockFunc(key, func() { array.Append(1) - gmlock.Unlock(key) - } + }) }() time.Sleep(50 * time.Millisecond) gtest.Assert(array.Len(), 1) - time.Sleep(80 * time.Millisecond) + time.Sleep(100 * time.Millisecond) gtest.Assert(array.Len(), 2) - time.Sleep(350 * time.Millisecond) - gtest.Assert(array.Len(), 3) }) } diff --git a/g/os/gmlock/gmlock_unit_rlock_test.go b/g/os/gmlock/gmlock_unit_rlock_test.go index 6363195ad..1b34e2be4 100644 --- a/g/os/gmlock/gmlock_unit_rlock_test.go +++ b/g/os/gmlock/gmlock_unit_rlock_test.go @@ -147,7 +147,7 @@ func Test_Locker_TryRLock(t *testing.T) { }) } -func Test_Locker_RLockFunc(t *testing.T) { +func Test_Locker_RLockFunc1(t *testing.T) { //RLockFunc before Lock gtest.Case(t, func() { key := "testRLockFuncBeforeLock" @@ -155,7 +155,7 @@ func Test_Locker_RLockFunc(t *testing.T) { go func() { gmlock.RLockFunc(key, func() { array.Append(1) - time.Sleep(50 * time.Millisecond) + time.Sleep(500 * time.Millisecond) array.Append(1) }) }() @@ -165,9 +165,10 @@ func Test_Locker_RLockFunc(t *testing.T) { array.Append(1) gmlock.Unlock(key) }() - time.Sleep(20 * time.Millisecond) + + time.Sleep(200 * time.Millisecond) gtest.Assert(array.Len(), 1) - time.Sleep(80 * time.Millisecond) + time.Sleep(800 * time.Millisecond) gtest.Assert(array.Len(), 3) }) @@ -193,6 +194,10 @@ func Test_Locker_RLockFunc(t *testing.T) { gtest.Assert(array.Len(), 2) }) +} + +func Test_Locker_RLockFunc2(t *testing.T) { + //Lock before RLockFuncs gtest.Case(t, func() { key := "testLockBeforeRLockFuncs" @@ -200,26 +205,29 @@ func Test_Locker_RLockFunc(t *testing.T) { go func() { gmlock.Lock(key) array.Append(1) - time.Sleep(50 * time.Millisecond) + //glog.Println("add1") + time.Sleep(500 * time.Millisecond) gmlock.Unlock(key) }() go func() { - time.Sleep(10 * time.Millisecond) + time.Sleep(100 * time.Millisecond) gmlock.RLockFunc(key, func() { array.Append(1) - time.Sleep(70 * time.Millisecond) + //glog.Println("add2") + time.Sleep(700 * time.Millisecond) }) }() go func() { - time.Sleep(10 * time.Millisecond) + time.Sleep(100 * time.Millisecond) gmlock.RLockFunc(key, func() { array.Append(1) - time.Sleep(70 * time.Millisecond) + //glog.Println("add3") + time.Sleep(700 * time.Millisecond) }) }() - time.Sleep(20 * time.Millisecond) + time.Sleep(200 * time.Millisecond) gtest.Assert(array.Len(), 1) - time.Sleep(70 * time.Millisecond) + time.Sleep(700 * time.Millisecond) gtest.Assert(array.Len(), 3) }) } diff --git a/g/os/gmlock/gmlock_mutex.go b/g/os/gmutex/gmutex.go similarity index 52% rename from g/os/gmlock/gmlock_mutex.go rename to g/os/gmutex/gmutex.go index 72a133e34..6217520e2 100644 --- a/g/os/gmlock/gmlock_mutex.go +++ b/g/os/gmutex/gmutex.go @@ -1,37 +1,35 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright 2018-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package gmlock +// Package gmutex implements graceful concurrent-safe mutex with more rich features. +package gmutex import ( - "runtime" - "sync" - "github.com/gogf/gf/g/container/gtype" + "math" + "runtime" ) -// The high level RWMutex. -// It wraps the sync.RWMutex to implements more rich features. +// The high level Mutex, which implements more rich features for mutex. type Mutex struct { - mu sync.RWMutex - wid *gtype.Int64 // Unique id, used for multiple and safe logic Unlock. - locking *gtype.Bool // Locking mark for atomic operation for *Lock and Try*Lock functions. - // There must be only one locking operation at the same time for concurrent safe purpose. - state *gtype.Int32 // Locking state: - // 0: writing lock false; - // -1: writing lock true; - // >=1: reading lock; + state *gtype.Int32 // Indicates the state of mutex. + writer *gtype.Int32 // Pending writer count. + reader *gtype.Int32 // Pending reader count. + writing chan struct{} // Channel used for writer blocking. + reading chan struct{} // Channel used for reader blocking. } -// NewMutex creates and returns a new mutex. -func NewMutex() *Mutex { +// New creates and returns a new mutex. +func New() *Mutex { return &Mutex{ - wid: gtype.NewInt64(), state: gtype.NewInt32(), - locking: gtype.NewBool(), + writer: gtype.NewInt32(), + reader: gtype.NewInt32(), + writing: make(chan struct{}, 1), + reading: make(chan struct{}, math.MaxInt32), } } @@ -39,15 +37,15 @@ func NewMutex() *Mutex { // If the lock is already locked for reading or writing, // Lock blocks until the lock is available. func (m *Mutex) Lock() { - if m.locking.Cas(false, true) { - m.mu.Lock() - // State should be changed after locks. - m.state.Set(-1) - m.wid.Add(1) - m.locking.Set(false) - } else { - runtime.Gosched() - m.Lock() + for { + // If there're no readers pending and writing lock currently, + // then do the writing lock checks. + if m.reader.Val() == 0 && m.state.Cas(0, -1) { + return + } + m.writer.Add(1) + <-m.writing + m.writer.Add(-1) } } @@ -55,7 +53,17 @@ func (m *Mutex) Lock() { // It is safe to be called multiple times if there's any locks or not. func (m *Mutex) Unlock() { if m.state.Cas(-1, 0) { - m.mu.Unlock() + // Writing lock unlocks, then first check the blocked readers. + if n := m.reader.Val(); n > 0 { + // If there're readers blocked, unlock them with preemption. + for ; n > 0; n-- { + m.reading <- struct{}{} + } + return + } + if m.writer.Val() > 0 { + m.writing <- struct{}{} + } } } @@ -63,14 +71,8 @@ func (m *Mutex) Unlock() { // It returns true if success, or if there's a write/reading lock on the mutex, // it returns false. func (m *Mutex) TryLock() bool { - if m.locking.Cas(false, true) { - if m.state.Cas(0, -1) { - m.mu.Lock() - m.wid.Add(1) - m.locking.Set(false) - return true - } - m.locking.Set(false) + if m.reader.Val() == 0 && m.state.Cas(0, -1) { + return true } return false } @@ -79,59 +81,106 @@ func (m *Mutex) TryLock() bool { // If the mutex is already locked for writing, // It blocks until the lock is available. func (m *Mutex) RLock() { - if m.locking.Cas(false, true) { - m.mu.RLock() - // State should be changed after locks. - m.state.Add(1) - m.locking.Set(false) - } else { - runtime.Gosched() - m.RLock() + var n int32 + for { + // If there're no writing lock and pending writers currently, + // then do the reading lock checks. + if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 { + if m.state.Cas(n, n+1) { + return + } else { + runtime.Gosched() + continue + } + } + // Or else pending the reader. + m.reader.Add(1) + <-m.reading + m.reader.Add(-1) } } // RUnlock unlocks the reading lock. // It is safe to be called multiple times if there's any locks or not. func (m *Mutex) RUnlock() { - if n := m.state.Val(); n >= 1 { - if m.state.Cas(n, n-1) { - m.mu.RUnlock() + var n int32 + for { + if n = m.state.Val(); n >= 1 { + if m.state.Cas(n, n-1) { + break + } else { + runtime.Gosched() + } } else { - m.RUnlock() + break } } + // Reading lock unlocks, then first check the blocked writers. + if n == 1 && m.writer.Val() > 0 { + // No readers blocked, then the writers can take place. + m.writing <- struct{}{} + } } // TryRLock tries locking the mutex for reading. // It returns true if success, or if there's a writing lock on the mutex, it returns false. func (m *Mutex) TryRLock() bool { - if m.locking.Cas(false, true) { - if m.state.Val() >= 0 { - m.mu.RLock() - m.state.Add(1) - m.locking.Set(false) - return true + var n int32 + for { + if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 { + if m.state.Cas(n, n+1) { + return true + } else { + runtime.Gosched() + } + } else { + return false } m.locking.Set(false) } - return false } -// IsLocked checks whether the mutex is locked by writing or reading lock. +// IsLocked checks whether the mutex is locked with writing or reading lock. +// Note that the result might be changed after it's called, +// so it cannot be the criterion for atomic operations. func (m *Mutex) IsLocked() bool { return m.state.Val() != 0 } -// IsRLocked checks whether the mutex is locked by writing lock. +// IsWLocked checks whether the mutex is locked by writing lock. +// Note that the result might be changed after it's called, +// so it cannot be the criterion for atomic operations. func (m *Mutex) IsWLocked() bool { return m.state.Val() < 0 } // IsRLocked checks whether the mutex is locked by reading lock. +// Note that the result might be changed after it's called, +// so it cannot be the criterion for atomic operations. func (m *Mutex) IsRLocked() bool { return m.state.Val() > 0 } +// LockFunc locks the mutex for writing with given callback function . +// If there's a write/reading lock the mutex, it will blocks until the lock is released. +// +// It releases the lock after is executed. +func (m *Mutex) LockFunc(f func()) { + m.Lock() + defer m.Unlock() + f() +} + +// RLockFunc locks the mutex for reading with given callback function . +// If there's a writing lock the mutex, it will blocks until the lock is released. +// +// It releases the lock after is executed. +func (m *Mutex) RLockFunc(f func()) { + m.RLock() + defer m.RUnlock() + f() +} + // TryLockFunc tries locking the mutex for writing with given callback function . // it returns true if success, or if there's a write/reading lock on the mutex, // it returns false. @@ -158,23 +207,3 @@ func (m *Mutex) TryRLockFunc(f func()) bool { } return false } - -// LockFunc locks the mutex for writing with given callback function . -// If there's a write/reading lock the mutex, it will blocks until the lock is released. -// -// It releases the lock after is executed. -func (m *Mutex) LockFunc(f func()) { - m.Lock() - defer m.Unlock() - f() -} - -// RLockFunc locks the mutex for reading with given callback function . -// If there's a writing lock the mutex, it will blocks until the lock is released. -// -// It releases the lock after is executed. -func (m *Mutex) RLockFunc(f func()) { - m.RLock() - defer m.RUnlock() - f() -} diff --git a/g/os/gmutex/gmutex_bench_test.go b/g/os/gmutex/gmutex_bench_test.go new file mode 100644 index 000000000..9b7473bd2 --- /dev/null +++ b/g/os/gmutex/gmutex_bench_test.go @@ -0,0 +1,67 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmutex_test + +import ( + "sync" + "testing" + + "github.com/gogf/gf/g/os/gmutex" +) + +var ( + mu = sync.Mutex{} + rwmu = sync.RWMutex{} + gmu = gmutex.New() +) + +func Benchmark_Mutex_LockUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + mu.Lock() + mu.Unlock() + } +} + +func Benchmark_RWMutex_LockUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + rwmu.Lock() + rwmu.Unlock() + } +} + +func Benchmark_RWMutex_RLockRUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + rwmu.RLock() + rwmu.RUnlock() + } +} + +func Benchmark_GMutex_LockUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + gmu.Lock() + gmu.Unlock() + } +} + +func Benchmark_GMutex_TryLock(b *testing.B) { + for i := 0; i < b.N; i++ { + gmu.TryLock() + } +} + +func Benchmark_GMutex_RLockRUnlock(b *testing.B) { + for i := 0; i < b.N; i++ { + gmu.RLock() + gmu.RUnlock() + } +} + +func Benchmark_GMutex_TryRLock(b *testing.B) { + for i := 0; i < b.N; i++ { + gmu.TryRLock() + } +} diff --git a/g/os/gmutex/gmutex_uinit_test.go b/g/os/gmutex/gmutex_uinit_test.go new file mode 100644 index 000000000..c3389c507 --- /dev/null +++ b/g/os/gmutex/gmutex_uinit_test.go @@ -0,0 +1,258 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gmutex_test + +import ( + "testing" + "time" + + "github.com/gogf/gf/g/container/garray" + "github.com/gogf/gf/g/os/gmutex" + "github.com/gogf/gf/g/test/gtest" +) + +func Test_Mutex_RUnlock(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + for index := 0; index < 1000; index++ { + go func() { + mu.RLockFunc(func() { + time.Sleep(100 * time.Millisecond) + }) + }() + } + time.Sleep(10 * time.Millisecond) + gtest.Assert(mu.IsRLocked(), true) + gtest.Assert(mu.IsLocked(), true) + gtest.Assert(mu.IsWLocked(), false) + for index := 0; index < 1000; index++ { + go func() { + mu.RUnlock() + }() + } + time.Sleep(150 * time.Millisecond) + gtest.Assert(mu.IsRLocked(), false) + + }) +} + +func Test_Mutex_IsLocked(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + go func() { + mu.LockFunc(func() { + time.Sleep(100 * time.Millisecond) + }) + }() + time.Sleep(10 * time.Millisecond) + gtest.Assert(mu.IsLocked(), true) + gtest.Assert(mu.IsWLocked(), true) + gtest.Assert(mu.IsRLocked(), false) + time.Sleep(110 * time.Millisecond) + gtest.Assert(mu.IsLocked(), false) + gtest.Assert(mu.IsWLocked(), false) + + go func() { + mu.RLockFunc(func() { + time.Sleep(100 * time.Millisecond) + }) + }() + time.Sleep(10 * time.Millisecond) + gtest.Assert(mu.IsRLocked(), true) + gtest.Assert(mu.IsLocked(), true) + gtest.Assert(mu.IsWLocked(), false) + time.Sleep(110 * time.Millisecond) + gtest.Assert(mu.IsRLocked(), false) + }) +} + +func Test_Mutex_Unlock(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.LockFunc(func() { + array.Append(1) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.LockFunc(func() { + array.Append(1) + }) + }() + + go func() { + time.Sleep(60 * time.Millisecond) + mu.Unlock() + mu.Unlock() + mu.Unlock() + }() + + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 3) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} + +func Test_Mutex_LockFunc(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.LockFunc(func() { + array.Append(1) + }) + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + }) +} + +func Test_Mutex_TryLockFunc(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.TryLockFunc(func() { + array.Append(1) + }) + }() + go func() { + time.Sleep(110 * time.Millisecond) + mu.TryLockFunc(func() { + array.Append(1) + }) + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + }) +} + +func Test_Mutex_RLockFunc(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.RLockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + time.Sleep(20 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(50 * time.Millisecond) + gtest.Assert(array.Len(), 2) + }) + + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + time.Sleep(50 * time.Millisecond) + mu.RLockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.RLockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + go func() { + time.Sleep(50 * time.Millisecond) + mu.RLockFunc(func() { + array.Append(1) + time.Sleep(100 * time.Millisecond) + }) + }() + gtest.Assert(array.Len(), 0) + time.Sleep(80 * time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} + +func Test_Mutex_TryRLockFunc(t *testing.T) { + gtest.Case(t, func() { + mu := gmutex.New() + array := garray.New() + go func() { + mu.LockFunc(func() { + array.Append(1) + time.Sleep(500 * time.Millisecond) + }) + }() + go func() { + time.Sleep(200 * time.Millisecond) + mu.TryRLockFunc(func() { + array.Append(1) + }) + }() + go func() { + time.Sleep(700 * time.Millisecond) + mu.TryRLockFunc(func() { + array.Append(1) + }) + }() + go func() { + time.Sleep(700 * time.Millisecond) + mu.TryRLockFunc(func() { + array.Append(1) + }) + }() + time.Sleep(100 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(500 * time.Millisecond) + gtest.Assert(array.Len(), 1) + time.Sleep(500 * time.Millisecond) + gtest.Assert(array.Len(), 3) + }) +} diff --git a/g/os/grpool/grpool.go b/g/os/grpool/grpool.go index b37a567c7..bb12a265d 100644 --- a/g/os/grpool/grpool.go +++ b/g/os/grpool/grpool.go @@ -8,6 +8,8 @@ package grpool import ( + "errors" + "github.com/gogf/gf/g/container/glist" "github.com/gogf/gf/g/container/gtype" ) @@ -41,35 +43,46 @@ func New(limit ...int) *Pool { // Add pushes a new job to the pool using default goroutine pool. // The job will be executed asynchronously. -func Add(f func()) { - pool.Add(f) +func Add(f func()) error { + return pool.Add(f) } // Size returns current goroutine count of default goroutine pool. func Size() int { - return pool.count.Val() + return pool.Size() } // Jobs returns current job count of default goroutine pool. func Jobs() int { - return pool.list.Len() + return pool.Jobs() } // Add pushes a new job to the pool. // The job will be executed asynchronously. -func (p *Pool) Add(f func()) { +func (p *Pool) Add(f func()) error { + for p.closed.Val() { + return errors.New("pool closed") + } p.list.PushFront(f) - // check whether to create a new goroutine or not. - if p.count.Val() == p.limit { - return + var n int + for { + n = p.count.Val() + if p.limit != -1 && n >= p.limit { + return nil + } + if p.count.Cas(n, n+1) { + break + } } - // ensure atomicity. - if p.limit != -1 && p.count.Add(1) > p.limit { - p.count.Add(-1) - return - } - // fork a new goroutine to consume the job list. p.fork() + return nil +} + +// Cap returns the capacity of the pool. +// This capacity is defined when pool is created. +// If it returns -1 means no limit. +func (p *Pool) Cap() int { + return p.limit } // Size returns current goroutine count of the pool. @@ -97,6 +110,11 @@ func (p *Pool) fork() { }() } +// IsClosed returns if pool is closed. +func (p *Pool) IsClosed() bool { + return p.closed.Val() +} + // Close closes the goroutine pool, which makes all goroutines exit. func (p *Pool) Close() { p.closed.Set(true) diff --git a/g/test/gtest/gtest_test.go b/g/test/gtest/gtest_test.go index 92ba83f7d..6b64ad151 100644 --- a/g/test/gtest/gtest_test.go +++ b/g/test/gtest/gtest_test.go @@ -7,12 +7,14 @@ package gtest_test import ( - "github.com/gogf/gf/g/test/gtest" "testing" + + "github.com/gogf/gf/g/test/gtest" ) func TestCase(t *testing.T) { gtest.Case(t, func() { gtest.Assert(1, 1) + gtest.AssertNE(1, 0) }) } diff --git a/g/text/gregex/gregex_z_bench_test.go b/g/text/gregex/gregex_z_bench_test.go index 8668ce37f..c8cfa553d 100644 --- a/g/text/gregex/gregex_z_bench_test.go +++ b/g/text/gregex/gregex_z_bench_test.go @@ -9,9 +9,10 @@ package gregex_test import ( - "github.com/gogf/gf/g/text/gregex" "regexp" "testing" + + "github.com/gogf/gf/g/text/gregex" ) var pattern = `(\w+).+\-\-\s*(.+)` diff --git a/g/util/gconv/gconv_z_all_test.go b/g/util/gconv/gconv_z_all_test.go new file mode 100644 index 000000000..c47eed1e0 --- /dev/null +++ b/g/util/gconv/gconv_z_all_test.go @@ -0,0 +1,1368 @@ +package gconv_test + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/util/gconv" + "testing" + "time" +) + +type apiString interface { + String() string +} +type S struct { +} + +func (s S) String() string { + return "22222" +} + +type apiError interface { + Error() string +} +type S1 struct { +} + +func (s1 S1) Error() string { + return "22222" +} + +func Test_Bool_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Bool(i), false) + gtest.AssertEQ(gconv.Bool(false), false) + gtest.AssertEQ(gconv.Bool(nil), false) + gtest.AssertEQ(gconv.Bool(0), false) + gtest.AssertEQ(gconv.Bool("0"), false) + gtest.AssertEQ(gconv.Bool(""), false) + gtest.AssertEQ(gconv.Bool("false"), false) + gtest.AssertEQ(gconv.Bool("off"), false) + gtest.AssertEQ(gconv.Bool([]byte{}), false) + gtest.AssertEQ(gconv.Bool([]string{}), false) + gtest.AssertEQ(gconv.Bool([2]int{1, 2}), true) + gtest.AssertEQ(gconv.Bool([]interface{}{}), false) + gtest.AssertEQ(gconv.Bool([]map[int]int{}), false) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Bool(countryCapitalMap), true) + + gtest.AssertEQ(gconv.Bool("1"), true) + gtest.AssertEQ(gconv.Bool("on"), true) + gtest.AssertEQ(gconv.Bool(1), true) + gtest.AssertEQ(gconv.Bool(123.456), true) + gtest.AssertEQ(gconv.Bool(boolStruct{}), true) + gtest.AssertEQ(gconv.Bool(&boolStruct{}), true) + }) +} + +func Test_Int_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Int(i), 0) + gtest.AssertEQ(gconv.Int(false), 0) + gtest.AssertEQ(gconv.Int(nil), 0) + gtest.Assert(gconv.Int(nil), 0) + gtest.AssertEQ(gconv.Int(0), 0) + gtest.AssertEQ(gconv.Int("0"), 0) + gtest.AssertEQ(gconv.Int(""), 0) + gtest.AssertEQ(gconv.Int("false"), 0) + gtest.AssertEQ(gconv.Int("off"), 0) + gtest.AssertEQ(gconv.Int([]byte{}), 0) + gtest.AssertEQ(gconv.Int([]string{}), 0) + gtest.AssertEQ(gconv.Int([2]int{1, 2}), 0) + gtest.AssertEQ(gconv.Int([]interface{}{}), 0) + gtest.AssertEQ(gconv.Int([]map[int]int{}), 0) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int(countryCapitalMap), 0) + + gtest.AssertEQ(gconv.Int("1"), 1) + gtest.AssertEQ(gconv.Int("on"), 0) + gtest.AssertEQ(gconv.Int(1), 1) + gtest.AssertEQ(gconv.Int(123.456), 123) + gtest.AssertEQ(gconv.Int(boolStruct{}), 0) + gtest.AssertEQ(gconv.Int(&boolStruct{}), 0) + }) +} + +func Test_Int8_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Int8(i), int8(0)) + gtest.AssertEQ(gconv.Int8(false), int8(0)) + gtest.AssertEQ(gconv.Int8(nil), int8(0)) + gtest.AssertEQ(gconv.Int8(0), int8(0)) + gtest.AssertEQ(gconv.Int8("0"), int8(0)) + gtest.AssertEQ(gconv.Int8(""), int8(0)) + gtest.AssertEQ(gconv.Int8("false"), int8(0)) + gtest.AssertEQ(gconv.Int8("off"), int8(0)) + gtest.AssertEQ(gconv.Int8([]byte{}), int8(0)) + gtest.AssertEQ(gconv.Int8([]string{}), int8(0)) + gtest.AssertEQ(gconv.Int8([2]int{1, 2}), int8(0)) + gtest.AssertEQ(gconv.Int8([]interface{}{}), int8(0)) + gtest.AssertEQ(gconv.Int8([]map[int]int{}), int8(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int8(countryCapitalMap), int8(0)) + + gtest.AssertEQ(gconv.Int8("1"), int8(1)) + gtest.AssertEQ(gconv.Int8("on"), int8(0)) + gtest.AssertEQ(gconv.Int8(int8(1)), int8(1)) + gtest.AssertEQ(gconv.Int8(123.456), int8(123)) + gtest.AssertEQ(gconv.Int8(boolStruct{}), int8(0)) + gtest.AssertEQ(gconv.Int8(&boolStruct{}), int8(0)) + }) +} + +func Test_Int16_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Int16(i), int16(0)) + gtest.AssertEQ(gconv.Int16(false), int16(0)) + gtest.AssertEQ(gconv.Int16(nil), int16(0)) + gtest.AssertEQ(gconv.Int16(0), int16(0)) + gtest.AssertEQ(gconv.Int16("0"), int16(0)) + gtest.AssertEQ(gconv.Int16(""), int16(0)) + gtest.AssertEQ(gconv.Int16("false"), int16(0)) + gtest.AssertEQ(gconv.Int16("off"), int16(0)) + gtest.AssertEQ(gconv.Int16([]byte{}), int16(0)) + gtest.AssertEQ(gconv.Int16([]string{}), int16(0)) + gtest.AssertEQ(gconv.Int16([2]int{1, 2}), int16(0)) + gtest.AssertEQ(gconv.Int16([]interface{}{}), int16(0)) + gtest.AssertEQ(gconv.Int16([]map[int]int{}), int16(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int16(countryCapitalMap), int16(0)) + + gtest.AssertEQ(gconv.Int16("1"), int16(1)) + gtest.AssertEQ(gconv.Int16("on"), int16(0)) + gtest.AssertEQ(gconv.Int16(int16(1)), int16(1)) + gtest.AssertEQ(gconv.Int16(123.456), int16(123)) + gtest.AssertEQ(gconv.Int16(boolStruct{}), int16(0)) + gtest.AssertEQ(gconv.Int16(&boolStruct{}), int16(0)) + }) +} + +func Test_Int32_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Int32(i), int32(0)) + gtest.AssertEQ(gconv.Int32(false), int32(0)) + gtest.AssertEQ(gconv.Int32(nil), int32(0)) + gtest.AssertEQ(gconv.Int32(0), int32(0)) + gtest.AssertEQ(gconv.Int32("0"), int32(0)) + gtest.AssertEQ(gconv.Int32(""), int32(0)) + gtest.AssertEQ(gconv.Int32("false"), int32(0)) + gtest.AssertEQ(gconv.Int32("off"), int32(0)) + gtest.AssertEQ(gconv.Int32([]byte{}), int32(0)) + gtest.AssertEQ(gconv.Int32([]string{}), int32(0)) + gtest.AssertEQ(gconv.Int32([2]int{1, 2}), int32(0)) + gtest.AssertEQ(gconv.Int32([]interface{}{}), int32(0)) + gtest.AssertEQ(gconv.Int32([]map[int]int{}), int32(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int32(countryCapitalMap), int32(0)) + + gtest.AssertEQ(gconv.Int32("1"), int32(1)) + gtest.AssertEQ(gconv.Int32("on"), int32(0)) + gtest.AssertEQ(gconv.Int32(int32(1)), int32(1)) + gtest.AssertEQ(gconv.Int32(123.456), int32(123)) + gtest.AssertEQ(gconv.Int32(boolStruct{}), int32(0)) + gtest.AssertEQ(gconv.Int32(&boolStruct{}), int32(0)) + }) +} + +func Test_Int64_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Int64("0x00e"), int64(14)) + gtest.Assert(gconv.Int64("022"), int64(18)) + + gtest.Assert(gconv.Int64(i), int64(0)) + gtest.Assert(gconv.Int64(true), 1) + gtest.Assert(gconv.Int64("1"), int64(1)) + gtest.Assert(gconv.Int64("0"), int64(0)) + gtest.Assert(gconv.Int64("X"), int64(0)) + gtest.Assert(gconv.Int64("x"), int64(0)) + gtest.Assert(gconv.Int64(int64(1)), int64(1)) + gtest.Assert(gconv.Int64(int(0)), int64(0)) + gtest.Assert(gconv.Int64(int8(0)), int64(0)) + gtest.Assert(gconv.Int64(int16(0)), int64(0)) + gtest.Assert(gconv.Int64(int32(0)), int64(0)) + gtest.Assert(gconv.Int64(uint64(0)), int64(0)) + gtest.Assert(gconv.Int64(uint32(0)), int64(0)) + gtest.Assert(gconv.Int64(uint16(0)), int64(0)) + gtest.Assert(gconv.Int64(uint8(0)), int64(0)) + gtest.Assert(gconv.Int64(uint(0)), int64(0)) + gtest.Assert(gconv.Int64(float32(0)), int64(0)) + + gtest.AssertEQ(gconv.Int64(false), int64(0)) + gtest.AssertEQ(gconv.Int64(nil), int64(0)) + gtest.AssertEQ(gconv.Int64(0), int64(0)) + gtest.AssertEQ(gconv.Int64("0"), int64(0)) + gtest.AssertEQ(gconv.Int64(""), int64(0)) + gtest.AssertEQ(gconv.Int64("false"), int64(0)) + gtest.AssertEQ(gconv.Int64("off"), int64(0)) + gtest.AssertEQ(gconv.Int64([]byte{}), int64(0)) + gtest.AssertEQ(gconv.Int64([]string{}), int64(0)) + gtest.AssertEQ(gconv.Int64([2]int{1, 2}), int64(0)) + gtest.AssertEQ(gconv.Int64([]interface{}{}), int64(0)) + gtest.AssertEQ(gconv.Int64([]map[int]int{}), int64(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Int64(countryCapitalMap), int64(0)) + + gtest.AssertEQ(gconv.Int64("1"), int64(1)) + gtest.AssertEQ(gconv.Int64("on"), int64(0)) + gtest.AssertEQ(gconv.Int64(int64(1)), int64(1)) + gtest.AssertEQ(gconv.Int64(123.456), int64(123)) + gtest.AssertEQ(gconv.Int64(boolStruct{}), int64(0)) + gtest.AssertEQ(gconv.Int64(&boolStruct{}), int64(0)) + }) +} + +func Test_Uint_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Uint(i), uint(0)) + gtest.AssertEQ(gconv.Uint(false), uint(0)) + gtest.AssertEQ(gconv.Uint(nil), uint(0)) + gtest.Assert(gconv.Uint(nil), uint(0)) + gtest.AssertEQ(gconv.Uint(uint(0)), uint(0)) + gtest.AssertEQ(gconv.Uint("0"), uint(0)) + gtest.AssertEQ(gconv.Uint(""), uint(0)) + gtest.AssertEQ(gconv.Uint("false"), uint(0)) + gtest.AssertEQ(gconv.Uint("off"), uint(0)) + gtest.AssertEQ(gconv.Uint([]byte{}), uint(0)) + gtest.AssertEQ(gconv.Uint([]string{}), uint(0)) + gtest.AssertEQ(gconv.Uint([2]int{1, 2}), uint(0)) + gtest.AssertEQ(gconv.Uint([]interface{}{}), uint(0)) + gtest.AssertEQ(gconv.Uint([]map[int]int{}), uint(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint(countryCapitalMap), uint(0)) + + gtest.AssertEQ(gconv.Uint("1"), uint(1)) + gtest.AssertEQ(gconv.Uint("on"), uint(0)) + gtest.AssertEQ(gconv.Uint(1), uint(1)) + gtest.AssertEQ(gconv.Uint(123.456), uint(123)) + gtest.AssertEQ(gconv.Uint(boolStruct{}), uint(0)) + gtest.AssertEQ(gconv.Uint(&boolStruct{}), uint(0)) + }) +} + +func Test_Uint8_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Uint8(i), uint8(0)) + gtest.AssertEQ(gconv.Uint8(uint8(1)), uint8(1)) + gtest.AssertEQ(gconv.Uint8(false), uint8(0)) + gtest.AssertEQ(gconv.Uint8(nil), uint8(0)) + gtest.AssertEQ(gconv.Uint8(0), uint8(0)) + gtest.AssertEQ(gconv.Uint8("0"), uint8(0)) + gtest.AssertEQ(gconv.Uint8(""), uint8(0)) + gtest.AssertEQ(gconv.Uint8("false"), uint8(0)) + gtest.AssertEQ(gconv.Uint8("off"), uint8(0)) + gtest.AssertEQ(gconv.Uint8([]byte{}), uint8(0)) + gtest.AssertEQ(gconv.Uint8([]string{}), uint8(0)) + gtest.AssertEQ(gconv.Uint8([2]int{1, 2}), uint8(0)) + gtest.AssertEQ(gconv.Uint8([]interface{}{}), uint8(0)) + gtest.AssertEQ(gconv.Uint8([]map[int]int{}), uint8(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint8(countryCapitalMap), uint8(0)) + + gtest.AssertEQ(gconv.Uint8("1"), uint8(1)) + gtest.AssertEQ(gconv.Uint8("on"), uint8(0)) + gtest.AssertEQ(gconv.Uint8(int8(1)), uint8(1)) + gtest.AssertEQ(gconv.Uint8(123.456), uint8(123)) + gtest.AssertEQ(gconv.Uint8(boolStruct{}), uint8(0)) + gtest.AssertEQ(gconv.Uint8(&boolStruct{}), uint8(0)) + }) +} + +func Test_Uint16_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Uint16(i), uint16(0)) + gtest.AssertEQ(gconv.Uint16(uint16(1)), uint16(1)) + gtest.AssertEQ(gconv.Uint16(false), uint16(0)) + gtest.AssertEQ(gconv.Uint16(nil), uint16(0)) + gtest.AssertEQ(gconv.Uint16(0), uint16(0)) + gtest.AssertEQ(gconv.Uint16("0"), uint16(0)) + gtest.AssertEQ(gconv.Uint16(""), uint16(0)) + gtest.AssertEQ(gconv.Uint16("false"), uint16(0)) + gtest.AssertEQ(gconv.Uint16("off"), uint16(0)) + gtest.AssertEQ(gconv.Uint16([]byte{}), uint16(0)) + gtest.AssertEQ(gconv.Uint16([]string{}), uint16(0)) + gtest.AssertEQ(gconv.Uint16([2]int{1, 2}), uint16(0)) + gtest.AssertEQ(gconv.Uint16([]interface{}{}), uint16(0)) + gtest.AssertEQ(gconv.Uint16([]map[int]int{}), uint16(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint16(countryCapitalMap), uint16(0)) + + gtest.AssertEQ(gconv.Uint16("1"), uint16(1)) + gtest.AssertEQ(gconv.Uint16("on"), uint16(0)) + gtest.AssertEQ(gconv.Uint16(int16(1)), uint16(1)) + gtest.AssertEQ(gconv.Uint16(123.456), uint16(123)) + gtest.AssertEQ(gconv.Uint16(boolStruct{}), uint16(0)) + gtest.AssertEQ(gconv.Uint16(&boolStruct{}), uint16(0)) + }) +} + +func Test_Uint32_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Uint32(i), uint32(0)) + gtest.AssertEQ(gconv.Uint32(uint32(1)), uint32(1)) + gtest.AssertEQ(gconv.Uint32(false), uint32(0)) + gtest.AssertEQ(gconv.Uint32(nil), uint32(0)) + gtest.AssertEQ(gconv.Uint32(0), uint32(0)) + gtest.AssertEQ(gconv.Uint32("0"), uint32(0)) + gtest.AssertEQ(gconv.Uint32(""), uint32(0)) + gtest.AssertEQ(gconv.Uint32("false"), uint32(0)) + gtest.AssertEQ(gconv.Uint32("off"), uint32(0)) + gtest.AssertEQ(gconv.Uint32([]byte{}), uint32(0)) + gtest.AssertEQ(gconv.Uint32([]string{}), uint32(0)) + gtest.AssertEQ(gconv.Uint32([2]int{1, 2}), uint32(0)) + gtest.AssertEQ(gconv.Uint32([]interface{}{}), uint32(0)) + gtest.AssertEQ(gconv.Uint32([]map[int]int{}), uint32(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint32(countryCapitalMap), uint32(0)) + + gtest.AssertEQ(gconv.Uint32("1"), uint32(1)) + gtest.AssertEQ(gconv.Uint32("on"), uint32(0)) + gtest.AssertEQ(gconv.Uint32(int32(1)), uint32(1)) + gtest.AssertEQ(gconv.Uint32(123.456), uint32(123)) + gtest.AssertEQ(gconv.Uint32(boolStruct{}), uint32(0)) + gtest.AssertEQ(gconv.Uint32(&boolStruct{}), uint32(0)) + }) +} + +func Test_Uint64_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Uint64("0x00e"), uint64(14)) + gtest.Assert(gconv.Uint64("022"), uint64(18)) + + gtest.AssertEQ(gconv.Uint64(i), uint64(0)) + gtest.AssertEQ(gconv.Uint64(true), uint64(1)) + gtest.Assert(gconv.Uint64("1"), int64(1)) + gtest.Assert(gconv.Uint64("0"), uint64(0)) + gtest.Assert(gconv.Uint64("X"), uint64(0)) + gtest.Assert(gconv.Uint64("x"), uint64(0)) + gtest.Assert(gconv.Uint64(int64(1)), uint64(1)) + gtest.Assert(gconv.Uint64(int(0)), uint64(0)) + gtest.Assert(gconv.Uint64(int8(0)), uint64(0)) + gtest.Assert(gconv.Uint64(int16(0)), uint64(0)) + gtest.Assert(gconv.Uint64(int32(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint64(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint32(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint16(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint8(0)), uint64(0)) + gtest.Assert(gconv.Uint64(uint(0)), uint64(0)) + gtest.Assert(gconv.Uint64(float32(0)), uint64(0)) + + gtest.AssertEQ(gconv.Uint64(false), uint64(0)) + gtest.AssertEQ(gconv.Uint64(nil), uint64(0)) + gtest.AssertEQ(gconv.Uint64(0), uint64(0)) + gtest.AssertEQ(gconv.Uint64("0"), uint64(0)) + gtest.AssertEQ(gconv.Uint64(""), uint64(0)) + gtest.AssertEQ(gconv.Uint64("false"), uint64(0)) + gtest.AssertEQ(gconv.Uint64("off"), uint64(0)) + gtest.AssertEQ(gconv.Uint64([]byte{}), uint64(0)) + gtest.AssertEQ(gconv.Uint64([]string{}), uint64(0)) + gtest.AssertEQ(gconv.Uint64([2]int{1, 2}), uint64(0)) + gtest.AssertEQ(gconv.Uint64([]interface{}{}), uint64(0)) + gtest.AssertEQ(gconv.Uint64([]map[int]int{}), uint64(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Uint64(countryCapitalMap), uint64(0)) + + gtest.AssertEQ(gconv.Uint64("1"), uint64(1)) + gtest.AssertEQ(gconv.Uint64("on"), uint64(0)) + gtest.AssertEQ(gconv.Uint64(int64(1)), uint64(1)) + gtest.AssertEQ(gconv.Uint64(123.456), uint64(123)) + gtest.AssertEQ(gconv.Uint64(boolStruct{}), uint64(0)) + gtest.AssertEQ(gconv.Uint64(&boolStruct{}), uint64(0)) + }) +} + +func Test_Float32_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Float32(i), float32(0)) + gtest.AssertEQ(gconv.Float32(false), float32(0)) + gtest.AssertEQ(gconv.Float32(nil), float32(0)) + gtest.AssertEQ(gconv.Float32(0), float32(0)) + gtest.AssertEQ(gconv.Float32("0"), float32(0)) + gtest.AssertEQ(gconv.Float32(""), float32(0)) + gtest.AssertEQ(gconv.Float32("false"), float32(0)) + gtest.AssertEQ(gconv.Float32("off"), float32(0)) + gtest.AssertEQ(gconv.Float32([]byte{}), float32(0)) + gtest.AssertEQ(gconv.Float32([]string{}), float32(0)) + gtest.AssertEQ(gconv.Float32([2]int{1, 2}), float32(0)) + gtest.AssertEQ(gconv.Float32([]interface{}{}), float32(0)) + gtest.AssertEQ(gconv.Float32([]map[int]int{}), float32(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Float32(countryCapitalMap), float32(0)) + + gtest.AssertEQ(gconv.Float32("1"), float32(1)) + gtest.AssertEQ(gconv.Float32("on"), float32(0)) + gtest.AssertEQ(gconv.Float32(float32(1)), float32(1)) + gtest.AssertEQ(gconv.Float32(123.456), float32(123.456)) + gtest.AssertEQ(gconv.Float32(boolStruct{}), float32(0)) + gtest.AssertEQ(gconv.Float32(&boolStruct{}), float32(0)) + }) +} + +func Test_Float64_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.Assert(gconv.Float64(i), float64(0)) + gtest.AssertEQ(gconv.Float64(false), float64(0)) + gtest.AssertEQ(gconv.Float64(nil), float64(0)) + gtest.AssertEQ(gconv.Float64(0), float64(0)) + gtest.AssertEQ(gconv.Float64("0"), float64(0)) + gtest.AssertEQ(gconv.Float64(""), float64(0)) + gtest.AssertEQ(gconv.Float64("false"), float64(0)) + gtest.AssertEQ(gconv.Float64("off"), float64(0)) + gtest.AssertEQ(gconv.Float64([]byte{}), float64(0)) + gtest.AssertEQ(gconv.Float64([]string{}), float64(0)) + gtest.AssertEQ(gconv.Float64([2]int{1, 2}), float64(0)) + gtest.AssertEQ(gconv.Float64([]interface{}{}), float64(0)) + gtest.AssertEQ(gconv.Float64([]map[int]int{}), float64(0)) + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.Float64(countryCapitalMap), float64(0)) + + gtest.AssertEQ(gconv.Float64("1"), float64(1)) + gtest.AssertEQ(gconv.Float64("on"), float64(0)) + gtest.AssertEQ(gconv.Float64(float64(1)), float64(1)) + gtest.AssertEQ(gconv.Float64(123.456), float64(123.456)) + gtest.AssertEQ(gconv.Float64(boolStruct{}), float64(0)) + gtest.AssertEQ(gconv.Float64(&boolStruct{}), float64(0)) + }) +} + +func Test_String_All(t *testing.T) { + gtest.Case(t, func() { + var s []rune + gtest.AssertEQ(gconv.String(s), "") + var i interface{} = nil + gtest.AssertEQ(gconv.String(i), "") + gtest.AssertEQ(gconv.String("1"), "1") + gtest.AssertEQ(gconv.String("0"), string("0")) + gtest.Assert(gconv.String("X"), string("X")) + gtest.Assert(gconv.String("x"), string("x")) + gtest.Assert(gconv.String(int64(1)), uint64(1)) + gtest.Assert(gconv.String(int(0)), string("0")) + gtest.Assert(gconv.String(int8(0)), string("0")) + gtest.Assert(gconv.String(int16(0)), string("0")) + gtest.Assert(gconv.String(int32(0)), string("0")) + gtest.Assert(gconv.String(uint64(0)), string("0")) + gtest.Assert(gconv.String(uint32(0)), string("0")) + gtest.Assert(gconv.String(uint16(0)), string("0")) + gtest.Assert(gconv.String(uint8(0)), string("0")) + gtest.Assert(gconv.String(uint(0)), string("0")) + gtest.Assert(gconv.String(float32(0)), string("0")) + gtest.AssertEQ(gconv.String(true), "true") + gtest.AssertEQ(gconv.String(false), "false") + gtest.AssertEQ(gconv.String(nil), "") + gtest.AssertEQ(gconv.String(0), string("0")) + gtest.AssertEQ(gconv.String("0"), string("0")) + gtest.AssertEQ(gconv.String(""), "") + gtest.AssertEQ(gconv.String("false"), "false") + gtest.AssertEQ(gconv.String("off"), string("off")) + gtest.AssertEQ(gconv.String([]byte{}), "") + gtest.AssertEQ(gconv.String([]string{}), "[]") + gtest.AssertEQ(gconv.String([2]int{1, 2}), "[1,2]") + gtest.AssertEQ(gconv.String([]interface{}{}), "[]") + gtest.AssertEQ(gconv.String(map[int]int{}), "{}") + + var countryCapitalMap = make(map[string]string) + /* map插入key - value对,各个国家对应的首都 */ + countryCapitalMap [ "France" ] = "巴黎" + countryCapitalMap [ "Italy" ] = "罗马" + countryCapitalMap [ "Japan" ] = "东京" + countryCapitalMap [ "India " ] = "新德里" + gtest.AssertEQ(gconv.String(countryCapitalMap), `{"France":"巴黎","India ":"新德里","Italy":"罗马","Japan":"东京"}`) + gtest.AssertEQ(gconv.String(int64(1)), "1") + gtest.AssertEQ(gconv.String(123.456), "123.456") + gtest.AssertEQ(gconv.String(boolStruct{}), "{}") + gtest.AssertEQ(gconv.String(&boolStruct{}), "{}") + + var info apiString + info = new(S) + gtest.AssertEQ(gconv.String(info), "22222") + var errinfo apiError + errinfo = new(S1) + gtest.AssertEQ(gconv.String(errinfo), "22222") + }) +} + +func Test_Runes_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Runes("www"), []int32{119, 119, 119}) + var s []rune + gtest.AssertEQ(gconv.Runes(s), nil) + }) +} + +func Test_Rune_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Rune("www"), int32(0)) + gtest.AssertEQ(gconv.Rune(int32(0)), int32(0)) + var s []rune + gtest.AssertEQ(gconv.Rune(s), int32(0)) + }) +} + +func Test_Bytes_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Bytes(nil), nil) + gtest.AssertEQ(gconv.Bytes(int32(0)), []uint8{0, 0, 0, 0}) + gtest.AssertEQ(gconv.Bytes("s"), []uint8{115}) + gtest.AssertEQ(gconv.Bytes([]byte("s")), []uint8{115}) + }) +} + +func Test_Byte_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Byte(uint8(0)), uint8(0)) + gtest.AssertEQ(gconv.Byte("s"), uint8(0)) + gtest.AssertEQ(gconv.Byte([]byte("s")), uint8(0)) + }) +} + +func Test_Convert_All(t *testing.T) { + gtest.Case(t, func() { + var i interface{} = nil + gtest.AssertEQ(gconv.Convert(i, "string"), "") + gtest.AssertEQ(gconv.Convert("1", "string"), "1") + gtest.Assert(gconv.Convert(int64(1), "int64"), int64(1)) + gtest.Assert(gconv.Convert(int(0), "int"), int(0)) + gtest.Assert(gconv.Convert(int8(0), "int8"), int8(0)) + gtest.Assert(gconv.Convert(int16(0), "int16"), int16(0)) + gtest.Assert(gconv.Convert(int32(0), "int32"), int32(0)) + gtest.Assert(gconv.Convert(uint64(0), "uint64"), uint64(0)) + gtest.Assert(gconv.Convert(uint32(0), "uint32"), uint32(0)) + gtest.Assert(gconv.Convert(uint16(0), "uint16"), uint16(0)) + gtest.Assert(gconv.Convert(uint8(0), "uint8"), uint8(0)) + gtest.Assert(gconv.Convert(uint(0), "uint"), uint(0)) + gtest.Assert(gconv.Convert(float32(0), "float32"), float32(0)) + gtest.Assert(gconv.Convert(float64(0), "float64"), float64(0)) + gtest.AssertEQ(gconv.Convert(true, "bool"), true) + gtest.AssertEQ(gconv.Convert([]byte{}, "[]byte"), []uint8{}) + gtest.AssertEQ(gconv.Convert([]string{}, "[]string"), []string{}) + gtest.AssertEQ(gconv.Convert([2]int{1, 2}, "[]int"), []int{0}) + gtest.AssertEQ(gconv.Convert("1989-01-02", "Time", "Y-m-d"), gconv.Time("1989-01-02", "Y-m-d")) + gtest.AssertEQ(gconv.Convert(1989, "Time"), gconv.Time("2033-01-11 04:00:00 +0800 CST")) + gtest.AssertEQ(gconv.Convert(gtime.Now(), "gtime.Time", 1), nil) + gtest.AssertEQ(gconv.Convert(1989, "gtime.Time"), gtime.Time{gconv.Time("2033-01-11 04:00:00 +0800 CST")}) + gtest.AssertEQ(gconv.Convert(gtime.Now(), "*gtime.Time", 1), nil) + gtest.AssertEQ(gconv.Convert(gtime.Now(), "GTime", 1), nil) + gtest.AssertEQ(gconv.Convert(1989, "*gtime.Time"), gconv.GTime(1989)) + gtest.AssertEQ(gconv.Convert(1989, "Duration"), time.Duration(int64(1989))) + gtest.AssertEQ(gconv.Convert("1989", "Duration"), time.Duration(int64(1989))) + gtest.AssertEQ(gconv.Convert("1989", ""), "1989") + }) +} + +func Test_Time_All(t *testing.T) { + gtest.Case(t, func() { + gtest.AssertEQ(gconv.Duration(""), time.Duration(int64(0))) + gtest.AssertEQ(gconv.GTime(""), gtime.New()) + }) +} + +func Test_Slice_All(t *testing.T) { + gtest.Case(t, func() { + value := 123.456 + gtest.AssertEQ(gconv.Ints(value), []int{123}) + gtest.AssertEQ(gconv.Ints(nil), nil) + gtest.AssertEQ(gconv.Ints([]string{"1", "2"}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]int{}), []int{}) + gtest.AssertEQ(gconv.Ints([]int8{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]int16{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]int32{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]int64{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]uint{1}), []int{1}) + gtest.AssertEQ(gconv.Ints([]uint8{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]uint16{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]uint32{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]uint64{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]bool{true}), []int{1}) + gtest.AssertEQ(gconv.Ints([]float32{1, 2}), []int{1, 2}) + gtest.AssertEQ(gconv.Ints([]float64{1, 2}), []int{1, 2}) + var inter []interface{} = make([]interface{}, 2) + gtest.AssertEQ(gconv.Ints(inter), []int{0, 0}) + + gtest.AssertEQ(gconv.Strings(value), []string{"123.456"}) + gtest.AssertEQ(gconv.Strings(nil), nil) + gtest.AssertEQ(gconv.Strings([]string{"1", "2"}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]int{1}), []string{"1"}) + gtest.AssertEQ(gconv.Strings([]int8{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]int16{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]int32{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]int64{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]uint{1}), []string{"1"}) + gtest.AssertEQ(gconv.Strings([]uint8{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]uint16{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]uint32{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]uint64{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]bool{true}), []string{"true"}) + gtest.AssertEQ(gconv.Strings([]float32{1, 2}), []string{"1", "2"}) + gtest.AssertEQ(gconv.Strings([]float64{1, 2}), []string{"1", "2"}) + var strer []interface{} = make([]interface{}, 2) + gtest.AssertEQ(gconv.Strings(strer), []string{" "}) + + gtest.AssertEQ(gconv.Floats(value), []float64{123.456}) + gtest.AssertEQ(gconv.Floats(nil), nil) + gtest.AssertEQ(gconv.Floats([]string{"1", "2"}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]int{1}), []float64{1}) + gtest.AssertEQ(gconv.Floats([]int8{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]int16{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]int32{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]int64{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]uint{1}), []float64{1}) + gtest.AssertEQ(gconv.Floats([]uint8{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]uint16{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]uint32{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]uint64{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]bool{true}), []float64{0}) + gtest.AssertEQ(gconv.Floats([]float32{1, 2}), []float64{1, 2}) + gtest.AssertEQ(gconv.Floats([]float64{1, 2}), []float64{1, 2}) + var floer []interface{} = make([]interface{}, 2) + gtest.AssertEQ(gconv.Floats(floer), []float64{0, 0}) + + gtest.AssertEQ(gconv.Interfaces(value), []interface{}{123.456}) + gtest.AssertEQ(gconv.Interfaces(nil), nil) + gtest.AssertEQ(gconv.Interfaces([]interface{}{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]string{"1"}), []interface{}{"1"}) + gtest.AssertEQ(gconv.Interfaces([]int{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]int8{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]int16{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]int32{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]int64{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint8{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint16{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint32{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]uint64{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]bool{true}), []interface{}{true}) + gtest.AssertEQ(gconv.Interfaces([]float32{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([]float64{1}), []interface{}{1}) + gtest.AssertEQ(gconv.Interfaces([1]int{1}), []interface{}{1}) + + type interSlice []int + slices := interSlice{1} + gtest.AssertEQ(gconv.Interfaces(slices), []interface{}{1}) + + gtest.AssertEQ(gconv.Maps(nil), nil) + gtest.AssertEQ(gconv.Maps([]map[string]interface{}{{"a": "1"},}), []map[string]interface{}{{"a": "1"},}) + gtest.AssertEQ(gconv.Maps(1223), []map[string]interface{}{{}}) + gtest.AssertEQ(gconv.Maps([]int{}), nil) + }) +} + +// 私有属性不会进行转换 +func Test_Slice_PrivateAttribute_All(t *testing.T) { + type User struct { + Id int + name string + Ad []interface{} + } + gtest.Case(t, func() { + user := &User{1, "john", []interface{}{2}} + gtest.Assert(gconv.Interfaces(user), g.Slice{1, []interface{}{2}}) + }) +} + +func Test_Map_Basic_All(t *testing.T) { + gtest.Case(t, func() { + m1 := map[string]string{ + "k": "v", + } + m2 := map[int]string{ + 3: "v", + } + m3 := map[float64]float32{ + 1.22: 3.1, + } + gtest.Assert(gconv.Map(m1), g.Map{ + "k": "v", + }) + gtest.Assert(gconv.Map(m2), g.Map{ + "3": "v", + }) + gtest.Assert(gconv.Map(m3), g.Map{ + "1.22": "3.1", + }) + gtest.AssertEQ(gconv.Map(nil), nil) + gtest.AssertEQ(gconv.Map(map[string]interface{}{"a": 1}), map[string]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[int]interface{}{1: 1}), map[string]interface{}{"1": 1}) + gtest.AssertEQ(gconv.Map(map[uint]interface{}{1: 1}), map[string]interface{}{"1": 1}) + gtest.AssertEQ(gconv.Map(map[uint]string{1: "1"}), map[string]interface{}{"1": "1"}) + + gtest.AssertEQ(gconv.Map(map[interface{}]interface{}{"a": 1}), map[interface{}]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[interface{}]string{"a": "1"}), map[interface{}]string{"a": "1"}) + gtest.AssertEQ(gconv.Map(map[interface{}]int{"a": 1}), map[interface{}]int{"a": 1}) + gtest.AssertEQ(gconv.Map(map[interface{}]uint{"a": 1}), map[interface{}]uint{"a": 1}) + gtest.AssertEQ(gconv.Map(map[interface{}]float32{"a": 1}), map[interface{}]float32{"a": 1}) + gtest.AssertEQ(gconv.Map(map[interface{}]float64{"a": 1}), map[interface{}]float64{"a": 1}) + + gtest.AssertEQ(gconv.Map(map[string]bool{"a": true}), map[string]interface{}{"a": true}) + gtest.AssertEQ(gconv.Map(map[string]int{"a": 1}), map[string]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[string]uint{"a": 1}), map[string]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[string]float32{"a": 1}), map[string]interface{}{"a": 1}) + gtest.AssertEQ(gconv.Map(map[string]float64{"a": 1}), map[string]interface{}{"a": 1}) + + }) +} + +func Test_Map_StructWithGconvTag_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name string + SiteUrl string `gconv:"-"` + NickName string `gconv:"nickname,omitempty"` + Pass1 string `gconv:"password1"` + Pass2 string `gconv:"password2"` + Ss []string `gconv:"ss"` + } + user1 := User{ + Uid: 100, + Name: "john", + SiteUrl: "https://goframe.org", + Pass1: "123", + Pass2: "456", + Ss: []string{"sss", "2222"}, + } + user2 := &user1 + map1 := gconv.Map(user1) + map2 := gconv.Map(user2) + gtest.Assert(map1["Uid"], 100) + gtest.Assert(map1["Name"], "john") + gtest.Assert(map1["SiteUrl"], nil) + gtest.Assert(map1["NickName"], nil) + gtest.Assert(map1["nickname"], nil) + gtest.Assert(map1["password1"], "123") + gtest.Assert(map1["password2"], "456") + gtest.Assert(map2["Uid"], 100) + gtest.Assert(map2["Name"], "john") + gtest.Assert(map2["SiteUrl"], nil) + gtest.Assert(map2["NickName"], nil) + gtest.Assert(map2["nickname"], nil) + gtest.Assert(map2["password1"], "123") + gtest.Assert(map2["password2"], "456") + }) +} + +func Test_Map_StructWithJsonTag_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name string + SiteUrl string `json:"-"` + NickName string `json:"nickname, omitempty"` + Pass1 string `json:"password1,newpassword"` + Pass2 string `json:"password2"` + Ss []string `json:"omitempty"` + ssb, ssa string + } + user1 := User{ + Uid: 100, + Name: "john", + SiteUrl: "https://goframe.org", + Pass1: "123", + Pass2: "456", + Ss: []string{"sss", "2222"}, + ssb: "11", + ssa: "222", + } + user3 := User{ + Uid: 100, + Name: "john", + NickName: "SSS", + SiteUrl: "https://goframe.org", + Pass1: "123", + Pass2: "456", + Ss: []string{"sss", "2222"}, + ssb: "11", + ssa: "222", + } + user2 := &user1 + _ = gconv.Map(user1, "Ss") + map1 := gconv.Map(user1, "json", "json2") + map2 := gconv.Map(user2) + map3 := gconv.Map(user3) + gtest.Assert(map1["Uid"], 100) + gtest.Assert(map1["Name"], "john") + gtest.Assert(map1["SiteUrl"], nil) + gtest.Assert(map1["NickName"], nil) + gtest.Assert(map1["nickname"], nil) + gtest.Assert(map1["password1"], "123") + gtest.Assert(map1["password2"], "456") + gtest.Assert(map2["Uid"], 100) + gtest.Assert(map2["Name"], "john") + gtest.Assert(map2["SiteUrl"], nil) + gtest.Assert(map2["NickName"], nil) + gtest.Assert(map2["nickname"], nil) + gtest.Assert(map2["password1"], "123") + gtest.Assert(map2["password2"], "456") + gtest.Assert(map3["NickName"], nil) + }) +} + +func Test_Map_PrivateAttribute_All(t *testing.T) { + type User struct { + Id int + name string + } + gtest.Case(t, func() { + user := &User{1, "john"} + gtest.Assert(gconv.Map(user), g.Map{"Id": 1}) + }) +} + +func Test_Map_StructInherit_All(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + S *string `json:"nickname2"` + } + + user := new(User) + user.Id = 100 + user.Nickname = "john" + user.CreateTime = "2019" + var s = "s" + user.S = &s + + m := gconv.MapDeep(user) + gtest.Assert(m["id"], user.Id) + gtest.Assert(m["nickname"], user.Nickname) + gtest.Assert(m["create_time"], user.CreateTime) + gtest.Assert(m["nickname2"], user.S) + }) +} + +func Test_Struct_Basic1_All(t *testing.T) { + gtest.Case(t, func() { + + type Score struct { + Name int + Result string + } + + type Score2 struct { + Name int + Result string + } + + type User struct { + Uid int + Name string + Site_Url string + NickName string + Pass1 string `gconv:"password1"` + Pass2 string `gconv:"password2"` + As *Score + Ass Score + Assb []interface{} + } + // 使用默认映射规则绑定属性值到对象 + user := new(User) + params1 := g.Map{ + "uid": 1, + "Name": "john", + "siteurl": "https://goframe.org", + "nick_name": "johng", + "PASS1": "123", + "PASS2": "456", + "As": g.Map{"Name": 1, "Result": "22222"}, + "Ass": &Score{11,"11"}, + "Assb": []string{"wwww"}, + } + _ = gconv.Struct(nil, user) + _ = gconv.Struct(params1, nil) + _ = gconv.Struct([]interface{}{nil}, user) + _ = gconv.Struct(user, []interface{}{nil}) + + var a = []interface{}{nil} + ab := &a + _ = gconv.Struct(params1, *ab) + var pi *int = nil + _ = gconv.Struct(params1, pi) + + _ = gconv.Struct(params1, user) + _ = gconv.Struct(params1, user, map[string]string{"uid": "Names"}) + _ = gconv.Struct(params1, user, map[string]string{"uid": "as"}) + + // 使用struct tag映射绑定属性值到对象 + user = new(User) + params2 := g.Map{ + "uid": 2, + "name": "smith", + "site-url": "https://goframe.org", + "nick name": "johng", + "password1": "111", + "password2": "222", + } + if err := gconv.Struct(params2, user); err != nil { + gtest.Error(err) + } + gtest.Assert(user, &User{ + Uid: 2, + Name: "smith", + Site_Url: "https://goframe.org", + NickName: "johng", + Pass1: "111", + Pass2: "222", + }) + }) +} + +// 使用默认映射规则绑定属性值到对象 +func Test_Struct_Basic2_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name string + SiteUrl string + Pass1 string + Pass2 string + } + user := new(User) + params := g.Map{ + "uid": 1, + "Name": "john", + "site_url": "https://goframe.org", + "PASS1": "123", + "PASS2": "456", + } + if err := gconv.Struct(params, user); err != nil { + gtest.Error(err) + } + gtest.Assert(user, &User{ + Uid: 1, + Name: "john", + SiteUrl: "https://goframe.org", + Pass1: "123", + Pass2: "456", + }) + }) +} + +// 带有指针的基础类型属性 +func Test_Struct_Basic3_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Uid int + Name *string + } + user := new(User) + params := g.Map{ + "uid": 1, + "Name": "john", + } + if err := gconv.Struct(params, user); err != nil { + gtest.Error(err) + } + gtest.Assert(user.Uid, 1) + gtest.Assert(*user.Name, "john") + }) +} + +// slice类型属性的赋值 +func Test_Struct_Attr_Slice_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + Scores []int + } + scores := []interface{}{99, 100, 60, 140} + user := new(User) + if err := gconv.Struct(g.Map{"Scores": scores}, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user, &User{ + Scores: []int{99, 100, 60, 140}, + }) + } + }) +} + +// 属性为struct对象 +func Test_Struct_Attr_Struct_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + } + + // 嵌套struct转换 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user, &User{ + Scores: Score{ + Name: "john", + Result: 100, + }, + }) + } + }) +} + +// 属性为struct对象指针 +func Test_Struct_Attr_Struct_Ptr_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores *Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + } + + // 嵌套struct转换 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user.Scores, &Score{ + Name: "john", + Result: 100, + }) + } + }) +} + +// 属性为struct对象slice +func Test_Struct_Attr_Struct_Slice1_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores []Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + } + + // 嵌套struct转换,属性为slice类型,数值为map类型 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user.Scores, []Score{ + { + Name: "john", + Result: 100, + }, + }) + } + }) +} + +// 属性为struct对象slice +func Test_Struct_Attr_Struct_Slice2_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores []Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": []interface{}{ + map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + map[string]interface{}{ + "Name": "smith", + "Result": 60, + }, + }, + } + + // 嵌套struct转换,属性为slice类型,数值为slice map类型 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(user.Scores, []Score{ + { + Name: "john", + Result: 100, + }, + { + Name: "smith", + Result: 60, + }, + }) + } + }) +} + +// 属性为struct对象slice ptr +func Test_Struct_Attr_Struct_Slice_Ptr_All(t *testing.T) { + gtest.Case(t, func() { + type Score struct { + Name string + Result int + } + type User struct { + Scores []*Score + } + + user := new(User) + scores := map[string]interface{}{ + "Scores": []interface{}{ + map[string]interface{}{ + "Name": "john", + "Result": 100, + }, + map[string]interface{}{ + "Name": "smith", + "Result": 60, + }, + }, + } + + // 嵌套struct转换,属性为slice类型,数值为slice map类型 + if err := gconv.Struct(scores, user); err != nil { + gtest.Error(err) + } else { + gtest.Assert(len(user.Scores), 2) + gtest.Assert(user.Scores[0], &Score{ + Name: "john", + Result: 100, + }) + gtest.Assert(user.Scores[1], &Score{ + Name: "smith", + Result: 60, + }) + } + }) +} + +func Test_Struct_PrivateAttribute_All(t *testing.T) { + type User struct { + Id int + name string + } + gtest.Case(t, func() { + user := new(User) + err := gconv.Struct(g.Map{"id": 1, "name": "john"}, user) + gtest.Assert(err, nil) + gtest.Assert(user.Id, 1) + gtest.Assert(user.name, "") + }) +} + +func Test_Struct_Deep_All(t *testing.T) { + gtest.Case(t, func() { + type Ids struct { + Id int `json:"id"` + Uid int `json:"uid"` + } + type Base struct { + Ids + CreateTime string `json:"create_time"` + } + type User struct { + Base + Passport string `json:"passport"` + Password string `json:"password"` + Nickname string `json:"nickname"` + } + data := g.Map{ + "id": 100, + "uid": 101, + "passport": "t1", + "password": "123456", + "nickname": "T1", + "create_time": "2019", + } + user := new(User) + gconv.StructDeep(data, user) + gtest.Assert(user.Id, 100) + gtest.Assert(user.Uid, 101) + gtest.Assert(user.Nickname, "T1") + gtest.Assert(user.CreateTime, "2019") + }) +} + +func Test_Struct_Time_All(t *testing.T) { + gtest.Case(t, func() { + type User struct { + CreateTime time.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": now, + }, user) + gtest.Assert(user.CreateTime.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime *time.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": &now, + }, user) + gtest.Assert(user.CreateTime.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime *gtime.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": &now, + }, user) + gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime gtime.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": &now, + }, user) + gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) + }) + + gtest.Case(t, func() { + type User struct { + CreateTime gtime.Time + } + now := time.Now() + user := new(User) + gconv.Struct(g.Map{ + "create_time": now, + }, user) + gtest.Assert(user.CreateTime.Time.UTC().String(), now.UTC().String()) + }) +} diff --git a/geg/net/ghttp/server/log/log.go b/geg/net/ghttp/server/log/log.go new file mode 100644 index 000000000..55ec3fb85 --- /dev/null +++ b/geg/net/ghttp/server/log/log.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/gogf/gf/g/net/ghttp" + "net/http" +) + +func main() { + s := ghttp.GetServer() + s.BindHandler("/log/handler", func(r *ghttp.Request) { + r.Response.WriteStatus(http.StatusNotFound, "文件找不到了") + }) + s.SetAccessLogEnabled(true) + s.SetErrorLogEnabled(true) + //s.SetLogHandler(func(r *ghttp.Request, error ...interface{}) { + // if len(error) > 0 { + // // 如果是错误日志 + // fmt.Println("错误产生了:", error[0]) + // } + // // 这里是请求日志 + // fmt.Println("请求处理完成,请求地址:", r.URL.String(), "请求结果:", r.Response.Status) + //}) + s.SetPort(8199) + s.Run() +} diff --git a/geg/net/ghttp/server/log/log_error.go b/geg/net/ghttp/server/log/log_error.go new file mode 100644 index 000000000..02cc6cd11 --- /dev/null +++ b/geg/net/ghttp/server/log/log_error.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/gogf/gf/g/net/ghttp" +) + +func main() { + s := ghttp.GetServer() + s.BindHandler("/log/error", func(r *ghttp.Request) { + if j := r.GetJson(); j != nil { + r.Response.Write(j.Get("test")) + } + }) + s.SetErrorLogEnabled(true) + s.SetPort(8199) + s.Run() +} diff --git a/geg/os/gflock/flock.go b/geg/os/gflock/flock.go index 7cd2d0e09..5d9271383 100644 --- a/geg/os/gflock/flock.go +++ b/geg/os/gflock/flock.go @@ -2,8 +2,9 @@ package main import ( "fmt" - "github.com/gogf/gf/third/github.com/theckman/go-flock" "time" + + "github.com/gogf/gf/third/github.com/theckman/go-flock" ) func main() { diff --git a/geg/os/gflock/gflock.go b/geg/os/gflock/gflock.go index f6c2b929a..07b3b8033 100644 --- a/geg/os/gflock/gflock.go +++ b/geg/os/gflock/gflock.go @@ -1,17 +1,18 @@ package main import ( + "time" + "github.com/gogf/gf/g/os/gflock" "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gproc" - "time" ) func main() { l := gflock.New("demo.lock") l.Lock() glog.Printf("locked by pid: %d", gproc.Pid()) - time.Sleep(3 * time.Second) + time.Sleep(10 * time.Second) l.UnLock() glog.Printf("unlocked by pid: %d", gproc.Pid()) } diff --git a/geg/other/test.go b/geg/other/test.go index 3690b2529..1b134b6c5 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,53 +1,24 @@ package main import ( - "fmt" - "time" - - "github.com/gogf/gf/g/container/garray" - "github.com/gogf/gf/g/os/gmlock" - "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" ) -func main() { - mu := gmlock.NewMutex() - array := garray.New() - go func() { - mu.LockFunc(func() { - array.Append(1) - time.Sleep(100 * time.Millisecond) - fmt.Println("====unlock") - }) - }() - go func() { - time.Sleep(50 * time.Millisecond) - fmt.Println("tryRLock1") - mu.TryRLockFunc(func() { - array.Append(1) - fmt.Println("tryRLock1 success") - }) - }() - go func() { - time.Sleep(150 * time.Millisecond) - fmt.Println("tryRLock2") - mu.TryRLockFunc(func() { - array.Append(1) - fmt.Println("tryRLock2 success") - }) - }() - go func() { - time.Sleep(150 * time.Millisecond) - fmt.Println("tryRLock3") - mu.TryRLockFunc(func() { - array.Append(1) - fmt.Println("tryRLock3 success") - }) - }() - time.Sleep(50 * time.Millisecond) - gtest.Assert(array.Len(), 1) - time.Sleep(50 * time.Millisecond) - gtest.Assert(array.Len(), 1) - time.Sleep(150 * time.Millisecond) - fmt.Println("====array len:", array.Len()) - gtest.Assert(array.Len(), 3) +type Order struct{} + +func (order *Order) Get(r *ghttp.Request) { + r.Response.Write("GET") +} + +func main() { + s := g.Server() + s.BindHookHandlerByMap("/api.v1/*any", map[string]ghttp.HandlerFunc{ + "BeforeServe": func(r *ghttp.Request) { + r.Response.CORSDefault() + }, + }) + s.BindObjectRest("/api.v1/{.struct}", new(Order)) + s.SetPort(8199) + s.Run() } diff --git a/geg/other/test2.go b/geg/other/test2.go index 521b49b46..8623d32ca 100644 --- a/geg/other/test2.go +++ b/geg/other/test2.go @@ -1,14 +1,39 @@ package main import ( - "fmt" - "github.com/gogf/gf/g/os/gmlock" - "time" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" ) -func main() { - key := "test3" - gmlock.Lock(key, 200*time.Millisecond) - fmt.Println("TryLock:", gmlock.TryLock(key)) - fmt.Println("TryLock:", gmlock.TryLock(key)) +type Schedule struct{} + +type Task struct{} + +func (c *Schedule) ListDir(r *ghttp.Request) { + r.Response.Writeln("ListDir") +} + +func (c *Task) Add(r *ghttp.Request) { + r.Response.Writeln("Add") +} + +func (c *Task) Task(r *ghttp.Request) { + r.Response.Writeln("Task") +} + +// 实现权限校验 +// 通过事件回调,类似于中间件机制,但是可控制的粒度更细,可以精准注册到路由规则 +func AuthHookHandler(r *ghttp.Request) { + // 如果权限校验失败,调用 r.ExitAll() 退出执行流程 +} + +func main() { + s := g.Server() + s.Group("/schedule").Bind([]ghttp.GroupItem{ + {"ALL", "*", AuthHookHandler, ghttp.HOOK_BEFORE_SERVE}, + {"POST", "/schedule", new(Schedule)}, + {"POST", "/task", new(Task)}, + }) + s.SetPort(8199) + s.Run() } diff --git a/go.mod b/go.mod index fd3232e72..ef37cb8d6 100644 --- a/go.mod +++ b/go.mod @@ -1 +1 @@ -module github.com/gogf/gf \ No newline at end of file +module github.com/gogf/gf