This commit is contained in:
John
2019-06-19 09:06:52 +08:00
parent ab48800401
commit 97df154b78
360 changed files with 27418 additions and 27371 deletions

View File

@ -6,4 +6,3 @@
// Package garray provides concurrent-safe/unsafe arrays.
package garray

View File

@ -7,13 +7,13 @@
package garray
type apiSliceInterface interface {
Slice() []interface{}
Slice() []interface{}
}
type apiSliceInt interface {
Slice() []int
Slice() []int
}
type apiSliceString interface {
Slice() []string
}
Slice() []string
}

View File

@ -7,13 +7,13 @@
package garray
import (
"bytes"
"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"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
"sort"
)
type IntArray struct {
@ -24,40 +24,40 @@ type IntArray struct {
// NewIntArray creates and returns an empty array.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewIntArray(unsafe...bool) *IntArray {
func NewIntArray(unsafe ...bool) *IntArray {
return NewIntArraySize(0, 0, unsafe...)
}
// NewIntArraySize create and returns an array with given size and cap.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray {
return &IntArray{
mu : rwmutex.New(unsafe...),
array : make([]int, size, cap),
}
func NewIntArraySize(size int, cap int, unsafe ...bool) *IntArray {
return &IntArray{
mu: rwmutex.New(unsafe...),
array: make([]int, size, cap),
}
}
// NewIntArrayFrom creates and returns an array with given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
func NewIntArrayFrom(array []int, unsafe ...bool) *IntArray {
return &IntArray{
mu : rwmutex.New(unsafe...),
array : array,
}
mu: rwmutex.New(unsafe...),
array: array,
}
}
// NewIntArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewIntArrayFromCopy(array []int, unsafe...bool) *IntArray {
newArray := make([]int, len(array))
copy(newArray, array)
return &IntArray{
mu : rwmutex.New(unsafe...),
array : newArray,
}
func NewIntArrayFromCopy(array []int, unsafe ...bool) *IntArray {
newArray := make([]int, len(array))
copy(newArray, array)
return &IntArray{
mu: rwmutex.New(unsafe...),
array: newArray,
}
}
// Get returns the value of the specified index,
@ -79,83 +79,83 @@ func (a *IntArray) Set(index int, value int) *IntArray {
// SetArray sets the underlying slice array with the given <array>.
func (a *IntArray) SetArray(array []int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
return a
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
return a
}
// Replace replaces the array items by given <array> from the beginning of array.
func (a *IntArray) Replace(array []int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
if max > len(a.array) {
max = len(a.array)
}
for i := 0; i < max; i++ {
a.array[i] = array[i]
}
return a
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
if max > len(a.array) {
max = len(a.array)
}
for i := 0; i < max; i++ {
a.array[i] = array[i]
}
return a
}
// Sum returns the sum of values in an array.
func (a *IntArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += v
}
return
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += v
}
return
}
// Sort sorts the array in increasing order.
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order
func (a *IntArray) Sort(reverse...bool) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(reverse) > 0 && reverse[0] {
sort.Slice(a.array, func(i, j int) bool {
if a.array[i] < a.array[j] {
return false
}
return true
})
} else {
sort.Ints(a.array)
}
return a
func (a *IntArray) Sort(reverse ...bool) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(reverse) > 0 && reverse[0] {
sort.Slice(a.array, func(i, j int) bool {
if a.array[i] < a.array[j] {
return false
}
return true
})
} else {
sort.Ints(a.array)
}
return a
}
// SortFunc sorts the array by custom function <less>.
func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return less(a.array[i], a.array[j])
})
return a
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return less(a.array[i], a.array[j])
})
return a
}
// InsertBefore inserts the <value> to the front of <index>.
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]int{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
rear := append([]int{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return a
return a
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]int{}, a.array[index + 1:]...)
a.array = append(a.array[0 : index + 1], value)
rear := append([]int{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return a
return a
}
// Remove removes an item by index.
@ -164,139 +164,139 @@ func (a *IntArray) Remove(index int) int {
defer a.mu.Unlock()
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
value := a.array[0]
a.array = a.array[1:]
return value
} else if index == len(a.array) - 1 {
value := a.array[index]
a.array = a.array[: index]
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
value := a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
}
// PushLeft pushes one or multiple items to the beginning of array.
func (a *IntArray) PushLeft(value...int) *IntArray {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
return a
func (a *IntArray) PushLeft(value ...int) *IntArray {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
return a
}
// PushRight pushes one or multiple items to the end of array.
// It equals to Append.
func (a *IntArray) PushRight(value...int) *IntArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
func (a *IntArray) PushRight(value ...int) *IntArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
}
// PopLeft pops and returns an item from the beginning of array.
func (a *IntArray) PopLeft() int {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1 : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1:]
return value
}
// PopRight pops and returns an item from the end of array.
func (a *IntArray) PopRight() int {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[: index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[:index]
return value
}
// PopRand randomly pops and return an item out of array.
func (a *IntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *IntArray) PopRands(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]int, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
}
return array
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]int, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
func (a *IntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0:size]
a.array = a.array[size:]
return value
}
// PopRights pops and returns <size> items from the end of array.
func (a *IntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index:]
a.array = a.array[:index]
return value
}
// 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 {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start : end]
}
return array
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start:end]
}
return array
}
// See PushRight.
func (a *IntArray) Append(value...int) *IntArray {
func (a *IntArray) Append(value ...int) *IntArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
a.mu.Unlock()
return a
}
// Len returns the length of array.
@ -325,26 +325,26 @@ func (a *IntArray) Slice() []int {
// Clone returns a new array, which is a copy of current array.
func (a *IntArray) Clone() (newArray *IntArray) {
a.mu.RLock()
array := make([]int, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewIntArrayFrom(array, !a.mu.IsSafe())
a.mu.RLock()
array := make([]int, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewIntArrayFrom(array, !a.mu.IsSafe())
}
// Clear deletes all items of current array.
func (a *IntArray) Clear() *IntArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]int, 0)
}
a.mu.Unlock()
return a
a.array = make([]int, 0)
}
a.mu.Unlock()
return a
}
// Contains checks whether a value exists in the array.
func (a *IntArray) Contains(value int) bool {
return a.Search(value) != -1
return a.Search(value) != -1
}
// Search searches array by <value>, returns the index of <value>,
@ -369,10 +369,10 @@ func (a *IntArray) Search(value int) int {
// Unique uniques the array, clear repeated items.
func (a *IntArray) Unique() *IntArray {
a.mu.Lock()
for i := 0; i < len(a.array) - 1; i++ {
for i := 0; i < len(a.array)-1; i++ {
for j := i + 1; j < len(a.array); j++ {
if a.array[i] == a.array[j] {
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
a.array = append(a.array[:j], a.array[j+1:]...)
}
}
}
@ -385,7 +385,7 @@ func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
return a
}
// RLockFunc locks reading by callback function <f>.
@ -393,7 +393,7 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
return a
}
// Merge merges <array> into current array.
@ -401,58 +401,64 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *IntArray) Merge(array interface{}) *IntArray {
switch v := array.(type) {
case *Array: a.Append(gconv.Ints(v.Slice())...)
case *IntArray: a.Append(gconv.Ints(v.Slice())...)
case *StringArray: a.Append(gconv.Ints(v.Slice())...)
case *SortedArray: a.Append(gconv.Ints(v.Slice())...)
case *SortedIntArray: a.Append(gconv.Ints(v.Slice())...)
case *SortedStringArray: a.Append(gconv.Ints(v.Slice())...)
default:
a.Append(gconv.Ints(array)...)
}
return a
switch v := array.(type) {
case *Array:
a.Append(gconv.Ints(v.Slice())...)
case *IntArray:
a.Append(gconv.Ints(v.Slice())...)
case *StringArray:
a.Append(gconv.Ints(v.Slice())...)
case *SortedArray:
a.Append(gconv.Ints(v.Slice())...)
case *SortedIntArray:
a.Append(gconv.Ints(v.Slice())...)
case *SortedStringArray:
a.Append(gconv.Ints(v.Slice())...)
default:
a.Append(gconv.Ints(array)...)
}
return a
}
// Fill fills an array with num entries of the value <value>,
// keys starting at the <startIndex> parameter.
func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
}
for i := startIndex; i < startIndex + num; i++ {
if i > len(a.array) - 1 {
a.array = append(a.array, value)
} else {
a.array[i] = value
}
}
return a
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {
a.array = append(a.array, value)
} else {
a.array[i] = value
}
}
return a
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
func (a *IntArray) Chunk(size int) [][]int {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]int
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]int
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
}
// Pad pads array to the specified length with <value>.
@ -460,105 +466,105 @@ func (a *IntArray) Chunk(size int) [][]int {
// If the absolute value of <size> is less than or equal to the length of the array
// then no padding takes place.
func (a *IntArray) Pad(size int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
return a
}
n := size
if size < 0 {
n = -size
}
n -= len(a.array)
tmp := make([]int, n)
for i := 0; i < n; i++ {
tmp[i] = value
}
if size > 0 {
a.array = append(a.array, tmp...)
} else {
a.array = append(tmp, a.array...)
}
return a
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
return a
}
n := size
if size < 0 {
n = -size
}
n -= len(a.array)
tmp := make([]int, n)
for i := 0; i < n; i++ {
tmp[i] = value
}
if size > 0 {
a.array = append(a.array, tmp...)
} else {
a.array = append(tmp, a.array...)
}
return a
}
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> 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:]
}
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()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands randomly returns <size> items from array(no deleting).
func (a *IntArray) Rands(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
}
return n
}
// Shuffle randomly shuffles the array.
func (a *IntArray) Shuffle() *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range grand.Perm(len(a.array)) {
a.array[i], a.array[v] = a.array[v], a.array[i]
}
return a
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range grand.Perm(len(a.array)) {
a.array[i], a.array[v] = a.array[v], a.array[i]
}
return a
}
// Reverse makes array with elements in reverse order.
func (a *IntArray) Reverse() *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
a.array[i], a.array[j] = a.array[j], a.array[i]
}
return a
a.mu.Lock()
defer a.mu.Unlock()
for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 {
a.array[i], a.array[j] = a.array[j], a.array[i]
}
return a
}
// Join joins array elements with a string <glue>.
func (a *IntArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array) - 1 {
buffer.WriteString(glue)
}
}
return buffer.String()
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
@ -577,4 +583,4 @@ func (a *IntArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
}

View File

@ -7,387 +7,387 @@
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"
"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"
)
type Array struct {
mu *rwmutex.RWMutex
array []interface{}
mu *rwmutex.RWMutex
array []interface{}
}
// New creates and returns an empty array.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func New(unsafe...bool) *Array {
return NewArraySize(0, 0, unsafe...)
func New(unsafe ...bool) *Array {
return NewArraySize(0, 0, unsafe...)
}
// See New.
func NewArray(unsafe...bool) *Array {
return NewArraySize(0, 0, unsafe...)
func NewArray(unsafe ...bool) *Array {
return NewArraySize(0, 0, unsafe...)
}
// NewArraySize create and returns an array with given size and cap.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewArraySize(size int, cap int, unsafe...bool) *Array {
return &Array{
mu : rwmutex.New(unsafe...),
array : make([]interface{}, size, cap),
}
func NewArraySize(size int, cap int, unsafe ...bool) *Array {
return &Array{
mu: rwmutex.New(unsafe...),
array: make([]interface{}, size, cap),
}
}
// See NewArrayFrom.
func NewFrom(array []interface{}, unsafe...bool) *Array {
return NewArrayFrom(array, unsafe...)
func NewFrom(array []interface{}, unsafe ...bool) *Array {
return NewArrayFrom(array, unsafe...)
}
// See NewArrayFromCopy.
func NewFromCopy(array []interface{}, unsafe...bool) *Array {
return NewArrayFromCopy(array, unsafe...)
func NewFromCopy(array []interface{}, unsafe ...bool) *Array {
return NewArrayFromCopy(array, unsafe...)
}
// NewArrayFrom creates and returns an array with given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
return &Array{
mu : rwmutex.New(unsafe...),
array : array,
}
func NewArrayFrom(array []interface{}, unsafe ...bool) *Array {
return &Array{
mu: rwmutex.New(unsafe...),
array: array,
}
}
// NewArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewArrayFromCopy(array []interface{}, unsafe...bool) *Array {
newArray := make([]interface{}, len(array))
copy(newArray, array)
return &Array{
mu : rwmutex.New(unsafe...),
array : newArray,
}
func NewArrayFromCopy(array []interface{}, unsafe ...bool) *Array {
newArray := make([]interface{}, len(array))
copy(newArray, array)
return &Array{
mu: rwmutex.New(unsafe...),
array: newArray,
}
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *Array) Get(index int) interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
}
// Set sets value to specified index.
func (a *Array) Set(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
a.array[index] = value
return a
a.mu.Lock()
defer a.mu.Unlock()
a.array[index] = value
return a
}
// SetArray sets the underlying slice array with the given <array>.
func (a *Array) SetArray(array []interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
return a
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
return a
}
// Replace replaces the array items by given <array> from the beginning of array.
func (a *Array) Replace(array []interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
if max > len(a.array) {
max = len(a.array)
}
for i := 0; i < max; i++ {
a.array[i] = array[i]
}
return a
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
if max > len(a.array) {
max = len(a.array)
}
for i := 0; i < max; i++ {
a.array[i] = array[i]
}
return a
}
// Sum returns the sum of values in an array.
func (a *Array) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
}
// SortFunc sorts the array by custom function <less>.
func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return less(a.array[i], a.array[j])
})
return a
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return less(a.array[i], a.array[j])
})
return a
}
// InsertBefore inserts the <value> to the front of <index>.
func (a *Array) InsertBefore(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]interface{}{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
return a
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]interface{}{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return a
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *Array) InsertAfter(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]interface{}{}, a.array[index + 1 : ]...)
a.array = append(a.array[0 : index + 1], value)
a.array = append(a.array, rear...)
return a
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]interface{}{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return a
}
// Remove removes an item by index.
func (a *Array) Remove(index int) interface{} {
a.mu.Lock()
defer a.mu.Unlock()
a.mu.Lock()
defer a.mu.Unlock()
// Determine array boundaries when deleting to improve deletion efficiency。
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
return value
} else if index == len(a.array) - 1 {
value := a.array[index]
a.array = a.array[: index]
return value
}
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
value := a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
}
// PushLeft pushes one or multiple items to the beginning of array.
func (a *Array) PushLeft(value...interface{}) *Array {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
return a
func (a *Array) PushLeft(value ...interface{}) *Array {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
return a
}
// PushRight pushes one or multiple items to the end of array.
// It equals to Append.
func (a *Array) PushRight(value...interface{}) *Array {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
func (a *Array) PushRight(value ...interface{}) *Array {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
}
// PopRand randomly pops and return an item out of array.
func (a *Array) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *Array) PopRands(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
}
return array
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
}
return array
}
// PopLeft pops and returns an item from the beginning of array.
func (a *Array) PopLeft() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1 : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1:]
return value
}
// PopRight pops and returns an item from the end of array.
func (a *Array) PopRight() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[: index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[:index]
return value
}
// PopLefts pops and returns <size> items from the beginning of array.
func (a *Array) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0:size]
a.array = a.array[size:]
return value
}
// PopRights pops and returns <size> items from the end of array.
func (a *Array) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index:]
a.array = a.array[:index]
return value
}
// 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{} {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start : end]
}
return array
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start:end]
}
return array
}
// See PushRight.
func (a *Array) Append(value...interface{}) *Array {
a.PushRight(value...)
return a
func (a *Array) Append(value ...interface{}) *Array {
a.PushRight(value...)
return a
}
// Len returns the length of array.
func (a *Array) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
}
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
func (a *Array) Slice() []interface{} {
array := ([]interface{})(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]interface{}, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
array := ([]interface{})(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]interface{}, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
}
// Clone returns a new array, which is a copy of current array.
func (a *Array) Clone() (newArray *Array) {
a.mu.RLock()
array := make([]interface{}, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewArrayFrom(array, !a.mu.IsSafe())
a.mu.RLock()
array := make([]interface{}, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewArrayFrom(array, !a.mu.IsSafe())
}
// Clear deletes all items of current array.
func (a *Array) Clear() *Array {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]interface{}, 0)
}
a.mu.Unlock()
return a
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]interface{}, 0)
}
a.mu.Unlock()
return a
}
// Contains checks whether a value exists in the array.
func (a *Array) Contains(value interface{}) bool {
return a.Search(value) != -1
return a.Search(value) != -1
}
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *Array) Search(value interface{}) int {
if len(a.array) == 0 {
return -1
}
a.mu.RLock()
result := -1
for index, v := range a.array {
if v == value {
result = index
break
}
}
a.mu.RUnlock()
if len(a.array) == 0 {
return -1
}
a.mu.RLock()
result := -1
for index, v := range a.array {
if v == value {
result = index
break
}
}
a.mu.RUnlock()
return result
return result
}
// Unique uniques the array, clear repeated items.
func (a *Array) Unique() *Array {
a.mu.Lock()
for i := 0; i < len(a.array) - 1; i++ {
for j := i + 1; j < len(a.array); j++ {
if a.array[i] == a.array[j] {
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
}
}
}
a.mu.Unlock()
return a
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
for j := i + 1; j < len(a.array); j++ {
if a.array[i] == a.array[j] {
a.array = append(a.array[:j], a.array[j+1:]...)
}
}
}
a.mu.Unlock()
return a
}
// LockFunc locks writing by callback function <f>.
func (a *Array) LockFunc(f func(array []interface{})) *Array {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
}
// RLockFunc locks reading by callback function <f>.
func (a *Array) RLockFunc(f func(array []interface{})) *Array {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
}
// Merge merges <array> into current array.
@ -395,58 +395,64 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *Array) Merge(array interface{}) *Array {
switch v := array.(type) {
case *Array: a.Append(gconv.Interfaces(v.Slice())...)
case *IntArray: a.Append(gconv.Interfaces(v.Slice())...)
case *StringArray: a.Append(gconv.Interfaces(v.Slice())...)
case *SortedArray: a.Append(gconv.Interfaces(v.Slice())...)
case *SortedIntArray: a.Append(gconv.Interfaces(v.Slice())...)
case *SortedStringArray: a.Append(gconv.Interfaces(v.Slice())...)
default:
a.Append(gconv.Interfaces(array)...)
}
return a
switch v := array.(type) {
case *Array:
a.Append(gconv.Interfaces(v.Slice())...)
case *IntArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *StringArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *SortedArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *SortedIntArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *SortedStringArray:
a.Append(gconv.Interfaces(v.Slice())...)
default:
a.Append(gconv.Interfaces(array)...)
}
return a
}
// Fill fills an array with num entries of the value <value>,
// keys starting at the <startIndex> parameter.
func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
}
for i := startIndex; i < startIndex + num; i++ {
if i > len(a.array) - 1 {
a.array = append(a.array, value)
} else {
a.array[i] = value
}
}
return a
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {
a.array = append(a.array, value)
} else {
a.array[i] = value
}
}
return a
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
func (a *Array) Chunk(size int) [][]interface{} {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]interface{}
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]interface{}
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
}
// Pad pads array to the specified length with <value>.
@ -454,121 +460,121 @@ func (a *Array) Chunk(size int) [][]interface{} {
// If the absolute value of <size> is less than or equal to the length of the array
// then no padding takes place.
func (a *Array) Pad(size int, val interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
return a
}
n := size
if size < 0 {
n = -size
}
n -= len(a.array)
tmp := make([]interface{}, n)
for i := 0; i < n; i++ {
tmp[i] = val
}
if size > 0 {
a.array = append(a.array, tmp...)
} else {
a.array = append(tmp, a.array...)
}
return a
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
return a
}
n := size
if size < 0 {
n = -size
}
n -= len(a.array)
tmp := make([]interface{}, n)
for i := 0; i < n; i++ {
tmp[i] = val
}
if size > 0 {
a.array = append(a.array, tmp...)
} else {
a.array = append(tmp, a.array...)
}
return a
}
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> 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:]
}
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()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands randomly returns <size> items from array(no deleting).
func (a *Array) Rands(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
}
return n
}
// Shuffle randomly shuffles the array.
func (a *Array) Shuffle() *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range grand.Perm(len(a.array)) {
a.array[i], a.array[v] = a.array[v], a.array[i]
}
return a
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range grand.Perm(len(a.array)) {
a.array[i], a.array[v] = a.array[v], a.array[i]
}
return a
}
// Reverse makes array with elements in reverse order.
func (a *Array) Reverse() *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
a.array[i], a.array[j] = a.array[j], a.array[i]
}
return a
a.mu.Lock()
defer a.mu.Unlock()
for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 {
a.array[i], a.array[j] = a.array[j], a.array[i]
}
return a
}
// Join joins array elements with a string <glue>.
func (a *Array) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array) - 1 {
buffer.WriteString(glue)
}
}
return buffer.String()
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
func (a *Array) CountValues() map[interface{}]int {
m := make(map[interface{}]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
m := make(map[interface{}]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
}
// String returns current array as a string.
func (a *Array) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}

View File

@ -7,14 +7,14 @@
package garray
import (
"bytes"
"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/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
"sort"
"strings"
)
type StringArray struct {
@ -25,40 +25,40 @@ type StringArray struct {
// NewStringArray creates and returns an empty array.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewStringArray(unsafe...bool) *StringArray {
return NewStringArraySize(0, 0, unsafe...)
func NewStringArray(unsafe ...bool) *StringArray {
return NewStringArraySize(0, 0, unsafe...)
}
// NewStringArraySize create and returns an array with given size and cap.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray {
return &StringArray{
mu : rwmutex.New(unsafe...),
array : make([]string, size, cap),
}
func NewStringArraySize(size int, cap int, unsafe ...bool) *StringArray {
return &StringArray{
mu: rwmutex.New(unsafe...),
array: make([]string, size, cap),
}
}
// NewStringArrayFrom creates and returns an array with given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
return &StringArray {
mu : rwmutex.New(unsafe...),
array : array,
func NewStringArrayFrom(array []string, unsafe ...bool) *StringArray {
return &StringArray{
mu: rwmutex.New(unsafe...),
array: array,
}
}
// NewStringArrayFromCopy creates and returns an array from a copy of given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewStringArrayFromCopy(array []string, unsafe...bool) *StringArray {
newArray := make([]string, len(array))
copy(newArray, array)
return &StringArray{
mu : rwmutex.New(unsafe...),
array : newArray,
}
func NewStringArrayFromCopy(array []string, unsafe ...bool) *StringArray {
newArray := make([]string, len(array))
copy(newArray, array)
return &StringArray{
mu: rwmutex.New(unsafe...),
array: newArray,
}
}
// Get returns the value of the specified index,
@ -75,88 +75,88 @@ func (a *StringArray) Set(index int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array[index] = value
return a
return a
}
// SetArray sets the underlying slice array with the given <array>.
func (a *StringArray) SetArray(array []string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
return a
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
return a
}
// Replace replaces the array items by given <array> from the beginning of array.
func (a *StringArray) Replace(array []string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
if max > len(a.array) {
max = len(a.array)
}
for i := 0; i < max; i++ {
a.array[i] = array[i]
}
return a
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
if max > len(a.array) {
max = len(a.array)
}
for i := 0; i < max; i++ {
a.array[i] = array[i]
}
return a
}
// Sum returns the sum of values in an array.
func (a *StringArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
}
// Sort sorts the array in increasing order.
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order
func (a *StringArray) Sort(reverse...bool) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(reverse) > 0 && reverse[0] {
sort.Slice(a.array, func(i, j int) bool {
if strings.Compare(a.array[i], a.array[j]) < 0 {
return false
}
return true
})
} else {
sort.Strings(a.array)
}
return a
func (a *StringArray) Sort(reverse ...bool) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(reverse) > 0 && reverse[0] {
sort.Slice(a.array, func(i, j int) bool {
if strings.Compare(a.array[i], a.array[j]) < 0 {
return false
}
return true
})
} else {
sort.Strings(a.array)
}
return a
}
// SortFunc sorts the array by custom function <less>.
func (a *StringArray) SortFunc(less func(v1, v2 string) bool) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return less(a.array[i], a.array[j])
})
return a
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return less(a.array[i], a.array[j])
})
return a
}
// InsertBefore inserts the <value> to the front of <index>.
func (a *StringArray) InsertBefore(index int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]string{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
rear := append([]string{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
return a
return a
}
// InsertAfter inserts the <value> to the back of <index>.
func (a *StringArray) InsertAfter(index int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]string{}, a.array[index + 1:]...)
a.array = append(a.array[ 0: index + 1], value)
rear := append([]string{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
a.array = append(a.array, rear...)
return a
return a
}
// Remove removes an item by index.
@ -165,139 +165,139 @@ func (a *StringArray) Remove(index int) string {
defer a.mu.Unlock()
// Determine array boundaries when deleting to improve deletion efficiency。
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
value := a.array[0]
a.array = a.array[1:]
return value
} else if index == len(a.array) - 1 {
value := a.array[index]
a.array = a.array[: index]
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
value := a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
}
// PushLeft pushes one or multiple items to the beginning of array.
func (a *StringArray) PushLeft(value...string) *StringArray {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
return a
func (a *StringArray) PushLeft(value ...string) *StringArray {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
return a
}
// PushRight pushes one or multiple items to the end of array.
// It equals to Append.
func (a *StringArray) PushRight(value...string) *StringArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
func (a *StringArray) PushRight(value ...string) *StringArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
}
// PopLeft pops and returns an item from the beginning of array.
func (a *StringArray) PopLeft() string {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1 : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1:]
return value
}
// PopRight pops and returns an item from the end of array.
func (a *StringArray) PopRight() string {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[: index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[:index]
return value
}
// PopRand randomly pops and return an item out of array.
func (a *StringArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *StringArray) PopRands(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]string, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
}
return array
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]string, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
func (a *StringArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0:size]
a.array = a.array[size:]
return value
}
// PopRights pops and returns <size> items from the end of array.
func (a *StringArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index:]
a.array = a.array[:index]
return value
}
// 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 {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start : end]
}
return array
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start:end]
}
return array
}
// See PushRight.
func (a *StringArray) Append(value...string) *StringArray {
func (a *StringArray) Append(value ...string) *StringArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
return a
}
// Len returns the length of array.
@ -326,26 +326,26 @@ func (a *StringArray) Slice() []string {
// Clone returns a new array, which is a copy of current array.
func (a *StringArray) Clone() (newArray *StringArray) {
a.mu.RLock()
array := make([]string, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewStringArrayFrom(array, !a.mu.IsSafe())
a.mu.RLock()
array := make([]string, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewStringArrayFrom(array, !a.mu.IsSafe())
}
// Clear deletes all items of current array.
func (a *StringArray) Clear() *StringArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]string, 0)
}
a.mu.Unlock()
return a
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]string, 0)
}
a.mu.Unlock()
return a
}
// Contains checks whether a value exists in the array.
func (a *StringArray) Contains(value string) bool {
return a.Search(value) != -1
return a.Search(value) != -1
}
// Search searches array by <value>, returns the index of <value>,
@ -369,10 +369,10 @@ func (a *StringArray) Search(value string) int {
// Unique uniques the array, clear repeated items.
func (a *StringArray) Unique() *StringArray {
a.mu.Lock()
for i := 0; i < len(a.array) - 1; i++ {
for i := 0; i < len(a.array)-1; i++ {
for j := i + 1; j < len(a.array); j++ {
if a.array[i] == a.array[j] {
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
a.array = append(a.array[:j], a.array[j+1:]...)
}
}
}
@ -385,7 +385,7 @@ func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
return a
}
// RLockFunc locks reading by callback function <f>.
@ -393,7 +393,7 @@ func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
return a
}
// Merge merges <array> into current array.
@ -401,58 +401,64 @@ func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *StringArray) Merge(array interface{}) *StringArray {
switch v := array.(type) {
case *Array: a.Append(gconv.Strings(v.Slice())...)
case *IntArray: a.Append(gconv.Strings(v.Slice())...)
case *StringArray: a.Append(gconv.Strings(v.Slice())...)
case *SortedArray: a.Append(gconv.Strings(v.Slice())...)
case *SortedIntArray: a.Append(gconv.Strings(v.Slice())...)
case *SortedStringArray: a.Append(gconv.Strings(v.Slice())...)
default:
a.Append(gconv.Strings(array)...)
}
return a
switch v := array.(type) {
case *Array:
a.Append(gconv.Strings(v.Slice())...)
case *IntArray:
a.Append(gconv.Strings(v.Slice())...)
case *StringArray:
a.Append(gconv.Strings(v.Slice())...)
case *SortedArray:
a.Append(gconv.Strings(v.Slice())...)
case *SortedIntArray:
a.Append(gconv.Strings(v.Slice())...)
case *SortedStringArray:
a.Append(gconv.Strings(v.Slice())...)
default:
a.Append(gconv.Strings(array)...)
}
return a
}
// Fill fills an array with num entries of the value <value>,
// keys starting at the <startIndex> parameter.
func (a *StringArray) Fill(startIndex int, num int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
}
for i := startIndex; i < startIndex + num; i++ {
if i > len(a.array) - 1 {
a.array = append(a.array, value)
} else {
a.array[i] = value
}
}
return a
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {
a.array = append(a.array, value)
} else {
a.array[i] = value
}
}
return a
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
func (a *StringArray) Chunk(size int) [][]string {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]string
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]string
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
}
// Pad pads array to the specified length with <value>.
@ -460,105 +466,105 @@ func (a *StringArray) Chunk(size int) [][]string {
// If the absolute value of <size> is less than or equal to the length of the array
// then no padding takes place.
func (a *StringArray) Pad(size int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
return a
}
n := size
if size < 0 {
n = -size
}
n -= len(a.array)
tmp := make([]string, n)
for i := 0; i < n; i++ {
tmp[i] = value
}
if size > 0 {
a.array = append(a.array, tmp...)
} else {
a.array = append(tmp, a.array...)
}
return a
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
return a
}
n := size
if size < 0 {
n = -size
}
n -= len(a.array)
tmp := make([]string, n)
for i := 0; i < n; i++ {
tmp[i] = value
}
if size > 0 {
a.array = append(a.array, tmp...)
} else {
a.array = append(tmp, a.array...)
}
return a
}
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> 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:]
}
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()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands randomly returns <size> items from array(no deleting).
func (a *StringArray) Rands(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
}
return n
}
// Shuffle randomly shuffles the array.
func (a *StringArray) Shuffle() *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range grand.Perm(len(a.array)) {
a.array[i], a.array[v] = a.array[v], a.array[i]
}
return a
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range grand.Perm(len(a.array)) {
a.array[i], a.array[v] = a.array[v], a.array[i]
}
return a
}
// Reverse makes array with elements in reverse order.
func (a *StringArray) Reverse() *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
a.array[i], a.array[j] = a.array[j], a.array[i]
}
return a
a.mu.Lock()
defer a.mu.Unlock()
for i, j := 0, len(a.array)-1; i < j; i, j = i+1, j-1 {
a.array[i], a.array[j] = a.array[j], a.array[i]
}
return a
}
// Join joins array elements with a string <glue>.
func (a *StringArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array) - 1 {
buffer.WriteString(glue)
}
}
return buffer.String()
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.

View File

@ -7,286 +7,286 @@
package garray
import (
"bytes"
"bytes"
"fmt"
"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"
"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.
type SortedIntArray struct {
mu *rwmutex.RWMutex
array []int
unique *gtype.Bool // Whether enable unique feature(false)
comparator func(v1, v2 int) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
mu *rwmutex.RWMutex
array []int
unique *gtype.Bool // Whether enable unique feature(false)
comparator func(v1, v2 int) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
}
// NewSortedIntArray creates and returns an empty sorted array.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedIntArray(unsafe...bool) *SortedIntArray {
return NewSortedIntArraySize(0, unsafe...)
func NewSortedIntArray(unsafe ...bool) *SortedIntArray {
return NewSortedIntArraySize(0, unsafe...)
}
// NewSortedIntArraySize create and returns an sorted array with given size and cap.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedIntArraySize(cap int, unsafe...bool) *SortedIntArray {
return &SortedIntArray {
mu : rwmutex.New(unsafe...),
array : make([]int, 0, cap),
unique : gtype.NewBool(),
comparator : func(v1, v2 int) int {
if v1 < v2 {
return -1
}
if v1 > v2 {
return 1
}
return 0
},
}
func NewSortedIntArraySize(cap int, unsafe ...bool) *SortedIntArray {
return &SortedIntArray{
mu: rwmutex.New(unsafe...),
array: make([]int, 0, cap),
unique: gtype.NewBool(),
comparator: func(v1, v2 int) int {
if v1 < v2 {
return -1
}
if v1 > v2 {
return 1
}
return 0
},
}
}
// NewIntArrayFrom creates and returns an sorted array with given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
a := NewSortedIntArraySize(0, unsafe...)
a.array = array
sort.Ints(a.array)
return a
func NewSortedIntArrayFrom(array []int, unsafe ...bool) *SortedIntArray {
a := NewSortedIntArraySize(0, unsafe...)
a.array = array
sort.Ints(a.array)
return a
}
// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedIntArrayFromCopy(array []int, unsafe...bool) *SortedIntArray {
newArray := make([]int, len(array))
copy(newArray, array)
return NewSortedIntArrayFrom(newArray, unsafe...)
func NewSortedIntArrayFromCopy(array []int, unsafe ...bool) *SortedIntArray {
newArray := make([]int, len(array))
copy(newArray, array)
return NewSortedIntArrayFrom(newArray, unsafe...)
}
// SetArray sets the underlying slice array with the given <array>.
func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Ints(a.array)
return a
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Ints(a.array)
return a
}
// Sort sorts the array in increasing order.
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order.
func (a *SortedIntArray) Sort() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Ints(a.array)
return a
a.mu.Lock()
defer a.mu.Unlock()
sort.Ints(a.array)
return a
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedIntArray) Add(values...int) *SortedIntArray {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
rear := append([]int{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
}
return a
func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
rear := append([]int{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
}
return a
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedIntArray) Get(index int) int {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
}
// Remove removes an item by index.
func (a *SortedIntArray) Remove(index int) int {
a.mu.Lock()
defer a.mu.Unlock()
a.mu.Lock()
defer a.mu.Unlock()
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
return value
} else if index == len(a.array) - 1 {
value := a.array[index]
a.array = a.array[: index]
return value
}
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
value := a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
}
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedIntArray) PopLeft() int {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1 : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1:]
return value
}
// PopRight pops and returns an item from the end of array.
func (a *SortedIntArray) PopRight() int {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[: index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[:index]
return value
}
// PopRand randomly pops and return an item out of array.
func (a *SortedIntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *SortedIntArray) PopRands(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]int, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
}
return array
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]int, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
func (a *SortedIntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0:size]
a.array = a.array[size:]
return value
}
// PopRights pops and returns <size> items from the end of array.
func (a *SortedIntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index:]
a.array = a.array[:index]
return value
}
// 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 {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start : end]
}
return array
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start:end]
}
return array
}
// Len returns the length of array.
func (a *SortedIntArray) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
}
// Sum returns the sum of values in an array.
func (a *SortedIntArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += v
}
return
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += v
}
return
}
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
func (a *SortedIntArray) Slice() []int {
array := ([]int)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]int, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
array := ([]int)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]int, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
}
// Contains checks whether a value exists in the array.
func (a *SortedIntArray) Contains(value int) bool {
return a.Search(value) != -1
return a.Search(value) != -1
}
// Search searches array by <value>, returns the index of <value>,
// or returns -1 if not exists.
func (a *SortedIntArray) Search(value int) (index int) {
if i, r := a.binSearch(value, true); r == 0 {
return i
}
return -1
if i, r := a.binSearch(value, true); r == 0 {
return i
}
return -1
}
// Binary search.
@ -295,93 +295,95 @@ func (a *SortedIntArray) Search(value int) (index int) {
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
// If <result> greater than 0, it means the value at <index> is greater than <value>.
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
}
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
switch {
case cmp < 0 : max = mid - 1
case cmp > 0 : min = mid + 1
default :
return mid, cmp
}
}
return mid, cmp
if len(a.array) == 0 {
return -1, -2
}
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
case cmp > 0:
min = mid + 1
default:
return mid, cmp
}
}
return mid, cmp
}
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
if unique && oldUnique != unique {
a.Unique()
}
return a
oldUnique := a.unique.Val()
a.unique.Set(unique)
if unique && oldUnique != unique {
a.Unique()
}
return a
}
// Unique uniques the array, clear repeated items.
func (a *SortedIntArray) Unique() *SortedIntArray {
a.mu.Lock()
i := 0
for {
if i == len(a.array) - 1 {
break
}
if a.comparator(a.array[i], a.array[i + 1]) == 0 {
a.array = append(a.array[ : i + 1], a.array[i + 1 + 1 : ]...)
} else {
i++
}
}
a.mu.Unlock()
return a
a.mu.Lock()
i := 0
for {
if i == len(a.array)-1 {
break
}
if a.comparator(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
} else {
i++
}
}
a.mu.Unlock()
return a
}
// Clone returns a new array, which is a copy of current array.
func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
a.mu.RLock()
array := make([]int, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedIntArrayFrom(array, !a.mu.IsSafe())
a.mu.RLock()
array := make([]int, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedIntArrayFrom(array, !a.mu.IsSafe())
}
// Clear deletes all items of current array.
func (a *SortedIntArray) Clear() *SortedIntArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]int, 0)
}
a.mu.Unlock()
return a
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]int, 0)
}
a.mu.Unlock()
return a
}
// LockFunc locks writing by callback function <f>.
func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
}
// RLockFunc locks reading by callback function <f>.
func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
}
// Merge merges <array> into current array.
@ -389,99 +391,105 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
switch v := array.(type) {
case *Array: a.Add(gconv.Ints(v.Slice())...)
case *IntArray: a.Add(gconv.Ints(v.Slice())...)
case *StringArray: a.Add(gconv.Ints(v.Slice())...)
case *SortedArray: a.Add(gconv.Ints(v.Slice())...)
case *SortedIntArray: a.Add(gconv.Ints(v.Slice())...)
case *SortedStringArray: a.Add(gconv.Ints(v.Slice())...)
default:
a.Add(gconv.Ints(array)...)
}
return a
switch v := array.(type) {
case *Array:
a.Add(gconv.Ints(v.Slice())...)
case *IntArray:
a.Add(gconv.Ints(v.Slice())...)
case *StringArray:
a.Add(gconv.Ints(v.Slice())...)
case *SortedArray:
a.Add(gconv.Ints(v.Slice())...)
case *SortedIntArray:
a.Add(gconv.Ints(v.Slice())...)
case *SortedStringArray:
a.Add(gconv.Ints(v.Slice())...)
default:
a.Add(gconv.Ints(array)...)
}
return a
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
func (a *SortedIntArray) Chunk(size int) [][]int {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]int
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]int
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
}
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> 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:]
}
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()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedIntArray) Rands(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
}
return n
}
// Join joins array elements with a string <glue>.
func (a *SortedIntArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array) - 1 {
buffer.WriteString(glue)
}
}
return buffer.String()
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
@ -500,4 +508,4 @@ func (a *SortedIntArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
}

View File

@ -7,22 +7,22 @@
package garray
import (
"bytes"
"bytes"
"fmt"
"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"
"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.
type SortedArray struct {
mu *rwmutex.RWMutex
array []interface{}
unique *gtype.Bool // Whether enable unique feature(false)
comparator func(v1, v2 interface{}) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
mu *rwmutex.RWMutex
array []interface{}
unique *gtype.Bool // Whether enable unique feature(false)
comparator func(v1, v2 interface{}) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
}
// NewSortedArray creates and returns an empty sorted array.
@ -31,254 +31,254 @@ type SortedArray struct {
// if it returns value < 0, means v1 < v2;
// if it returns value = 0, means v1 = v2;
// if it returns value > 0, means v1 > v2;
func NewSortedArray(comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
return NewSortedArraySize(0, comparator, unsafe...)
func NewSortedArray(comparator func(v1, v2 interface{}) int, unsafe ...bool) *SortedArray {
return NewSortedArraySize(0, comparator, unsafe...)
}
// NewSortedArraySize create and returns an sorted array with given size and cap.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedArraySize(cap int, comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
return &SortedArray{
mu : rwmutex.New(unsafe...),
unique : gtype.NewBool(),
array : make([]interface{}, 0, cap),
comparator : comparator,
}
func NewSortedArraySize(cap int, comparator func(v1, v2 interface{}) int, unsafe ...bool) *SortedArray {
return &SortedArray{
mu: rwmutex.New(unsafe...),
unique: gtype.NewBool(),
array: make([]interface{}, 0, cap),
comparator: comparator,
}
}
// NewSortedArrayFrom creates and returns an sorted array with given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedArrayFrom(array []interface{}, comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
a := NewSortedArraySize(0, comparator, unsafe...)
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
return a
func NewSortedArrayFrom(array []interface{}, comparator func(v1, v2 interface{}) int, unsafe ...bool) *SortedArray {
a := NewSortedArraySize(0, comparator, unsafe...)
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
return a
}
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedArrayFromCopy(array []interface{}, comparator func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
newArray := make([]interface{}, len(array))
copy(newArray, array)
return NewSortedArrayFrom(newArray, comparator, unsafe...)
func NewSortedArrayFromCopy(array []interface{}, comparator func(v1, v2 interface{}) int, unsafe ...bool) *SortedArray {
newArray := make([]interface{}, len(array))
copy(newArray, array)
return NewSortedArrayFrom(newArray, comparator, unsafe...)
}
// SetArray sets the underlying slice array with the given <array>.
func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
return a
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
return a
}
// Sort sorts the array in increasing order.
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order
func (a *SortedArray) Sort() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
return a
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
})
return a
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedArray) Add(values...interface{}) *SortedArray {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
rear := append([]interface{}{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
}
return a
func (a *SortedArray) Add(values ...interface{}) *SortedArray {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
rear := append([]interface{}{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
}
return a
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedArray) Get(index int) interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
}
// Remove removes an item by index.
func (a *SortedArray) Remove(index int) interface{} {
a.mu.Lock()
defer a.mu.Unlock()
a.mu.Lock()
defer a.mu.Unlock()
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
return value
} else if index == len(a.array) - 1 {
value := a.array[index]
a.array = a.array[: index]
return value
}
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
value := a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
}
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedArray) PopLeft() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1 : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1:]
return value
}
// PopRight pops and returns an item from the end of array.
func (a *SortedArray) PopRight() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[: index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[:index]
return value
}
// PopRand randomly pops and return an item out of array.
func (a *SortedArray) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *SortedArray) PopRands(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
}
return array
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]interface{}, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
func (a *SortedArray) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0:size]
a.array = a.array[size:]
return value
}
// PopRights pops and returns <size> items from the end of array.
func (a *SortedArray) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index:]
a.array = a.array[:index]
return value
}
// 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{} {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start : end]
}
return array
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start:end]
}
return array
}
// Sum returns the sum of values in an array.
func (a *SortedArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
}
// Len returns the length of array.
func (a *SortedArray) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
}
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
func (a *SortedArray) Slice() []interface{} {
array := ([]interface{})(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]interface{}, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
array := ([]interface{})(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]interface{}, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
}
// Contains checks whether a value exists in the array.
func (a *SortedArray) Contains(value interface{}) bool {
return a.Search(value) != -1
return a.Search(value) != -1
}
// Search searches array by <value>, returns the index of <value>,
@ -295,94 +295,96 @@ func (a *SortedArray) Search(value interface{}) (index int) {
// If <result> equals to 0, it means the value at <index> is equals to <value>.
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
// If <result> greater than 0, it means the value at <index> is greater than <value>.
func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result int) {
if len(a.array) == 0 {
return -1, -2
}
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
switch {
case cmp < 0 : max = mid - 1
case cmp > 0 : min = mid + 1
default :
return mid, cmp
}
}
return mid, cmp
func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
}
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
case cmp > 0:
min = mid + 1
default:
return mid, cmp
}
}
return mid, cmp
}
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedArray) SetUnique(unique bool) *SortedArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
if unique && oldUnique != unique {
a.Unique()
}
return a
oldUnique := a.unique.Val()
a.unique.Set(unique)
if unique && oldUnique != unique {
a.Unique()
}
return a
}
// Unique uniques the array, clear repeated items.
func (a *SortedArray) Unique() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
i := 0
for {
if i == len(a.array) - 1 {
break
}
if a.comparator(a.array[i], a.array[i + 1]) == 0 {
a.array = append(a.array[ : i + 1], a.array[i + 1 + 1 : ]...)
} else {
i++
}
}
return a
a.mu.Lock()
defer a.mu.Unlock()
i := 0
for {
if i == len(a.array)-1 {
break
}
if a.comparator(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
} else {
i++
}
}
return a
}
// Clone returns a new array, which is a copy of current array.
func (a *SortedArray) Clone() (newArray *SortedArray) {
a.mu.RLock()
array := make([]interface{}, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedArrayFrom(array, a.comparator, !a.mu.IsSafe())
a.mu.RLock()
array := make([]interface{}, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedArrayFrom(array, a.comparator, !a.mu.IsSafe())
}
// Clear deletes all items of current array.
func (a *SortedArray) Clear() *SortedArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]interface{}, 0)
}
a.mu.Unlock()
return a
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]interface{}, 0)
}
a.mu.Unlock()
return a
}
// LockFunc locks writing by callback function <f>.
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
}
// RLockFunc locks reading by callback function <f>.
func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
}
// Merge merges <array> into current array.
@ -390,99 +392,105 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedArray) Merge(array interface{}) *SortedArray {
switch v := array.(type) {
case *Array: a.Add(gconv.Interfaces(v.Slice())...)
case *IntArray: a.Add(gconv.Interfaces(v.Slice())...)
case *StringArray: a.Add(gconv.Interfaces(v.Slice())...)
case *SortedArray: a.Add(gconv.Interfaces(v.Slice())...)
case *SortedIntArray: a.Add(gconv.Interfaces(v.Slice())...)
case *SortedStringArray: a.Add(gconv.Interfaces(v.Slice())...)
default:
a.Add(gconv.Interfaces(array)...)
}
return a
switch v := array.(type) {
case *Array:
a.Add(gconv.Interfaces(v.Slice())...)
case *IntArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *StringArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *SortedArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *SortedIntArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *SortedStringArray:
a.Add(gconv.Interfaces(v.Slice())...)
default:
a.Add(gconv.Interfaces(array)...)
}
return a
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
func (a *SortedArray) Chunk(size int) [][]interface{} {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]interface{}
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]interface{}
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
}
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> 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:]
}
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()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedArray) Rands(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
}
return n
}
// Join joins array elements with a string <glue>.
func (a *SortedArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array) - 1 {
buffer.WriteString(glue)
}
}
return buffer.String()
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
@ -501,4 +509,4 @@ func (a *SortedArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
}

View File

@ -7,272 +7,272 @@
package garray
import (
"bytes"
"bytes"
"fmt"
"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"
"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.
type SortedStringArray struct {
mu *rwmutex.RWMutex
array []string
unique *gtype.Bool // Whether enable unique feature(false)
comparator func(v1, v2 string) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
mu *rwmutex.RWMutex
array []string
unique *gtype.Bool // Whether enable unique feature(false)
comparator func(v1, v2 string) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2)
}
// NewSortedStringArray creates and returns an empty sorted array.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedStringArray(unsafe...bool) *SortedStringArray {
return NewSortedStringArraySize(0, unsafe...)
func NewSortedStringArray(unsafe ...bool) *SortedStringArray {
return NewSortedStringArraySize(0, unsafe...)
}
// NewSortedStringArraySize create and returns an sorted array with given size and cap.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedStringArraySize(cap int, unsafe...bool) *SortedStringArray {
return &SortedStringArray {
mu : rwmutex.New(unsafe...),
array : make([]string, 0, cap),
unique : gtype.NewBool(),
comparator : func(v1, v2 string) int {
return strings.Compare(v1, v2)
},
}
func NewSortedStringArraySize(cap int, unsafe ...bool) *SortedStringArray {
return &SortedStringArray{
mu: rwmutex.New(unsafe...),
array: make([]string, 0, cap),
unique: gtype.NewBool(),
comparator: func(v1, v2 string) int {
return strings.Compare(v1, v2)
},
}
}
// NewSortedStringArrayFrom creates and returns an sorted array with given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray {
a := NewSortedStringArraySize(0, unsafe...)
a.array = array
sort.Strings(a.array)
return a
func NewSortedStringArrayFrom(array []string, unsafe ...bool) *SortedStringArray {
a := NewSortedStringArraySize(0, unsafe...)
a.array = array
sort.Strings(a.array)
return a
}
// NewSortedStringArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
// The parameter <unsafe> used to specify whether using array in un-concurrent-safety,
// which is false in default.
func NewSortedStringArrayFromCopy(array []string, unsafe...bool) *SortedStringArray {
newArray := make([]string, len(array))
copy(newArray, array)
return NewSortedStringArrayFrom(newArray, unsafe...)
func NewSortedStringArrayFromCopy(array []string, unsafe ...bool) *SortedStringArray {
newArray := make([]string, len(array))
copy(newArray, array)
return NewSortedStringArrayFrom(newArray, unsafe...)
}
// SetArray sets the underlying slice array with the given <array>.
func (a *SortedStringArray) SetArray(array []string) *SortedStringArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Strings(a.array)
return a
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Strings(a.array)
return a
}
// Sort sorts the array in increasing order.
// The parameter <reverse> controls whether sort
// in increasing order(default) or decreasing order.
func (a *SortedStringArray) Sort() *SortedStringArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Strings(a.array)
return a
a.mu.Lock()
defer a.mu.Unlock()
sort.Strings(a.array)
return a
}
// Add adds one or multiple values to sorted array, the array always keeps sorted.
func (a *SortedStringArray) Add(values...string) *SortedStringArray {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
rear := append([]string{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
}
return a
func (a *SortedStringArray) Add(values ...string) *SortedStringArray {
if len(values) == 0 {
return a
}
a.mu.Lock()
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
continue
}
if index < 0 {
a.array = append(a.array, value)
continue
}
if cmp > 0 {
index++
}
rear := append([]string{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
}
return a
}
// Get returns the value of the specified index,
// the caller should notice the boundary of the array.
func (a *SortedStringArray) Get(index int) string {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
}
// Remove removes an item by index.
func (a *SortedStringArray) Remove(index int) string {
a.mu.Lock()
defer a.mu.Unlock()
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
return value
} else if index == len(a.array) - 1 {
value := a.array[index]
a.array = a.array[: index]
return value
}
a.mu.Lock()
defer a.mu.Unlock()
// Determine array boundaries when deleting to improve deletion efficiency.
if index == 0 {
value := a.array[0]
a.array = a.array[1:]
return value
} else if index == len(a.array)-1 {
value := a.array[index]
a.array = a.array[:index]
return value
}
// If it is a non-boundary delete,
// it will involve the creation of an array,
// then the deletion is less efficient.
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
value := a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
return value
}
// PopLeft pops and returns an item from the beginning of array.
func (a *SortedStringArray) PopLeft() string {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1 : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1:]
return value
}
// PopRight pops and returns an item from the end of array.
func (a *SortedStringArray) PopRight() string {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[: index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[:index]
return value
}
// PopRand randomly pops and return an item out of array.
func (a *SortedStringArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
return a.Remove(grand.Intn(len(a.array)))
}
// PopRands randomly pops and returns <size> items out of array.
func (a *SortedStringArray) PopRands(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]string, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
}
return array
a.mu.Lock()
defer a.mu.Unlock()
if size > len(a.array) {
size = len(a.array)
}
array := make([]string, size)
for i := 0; i < size; i++ {
index := grand.Intn(len(a.array))
array[i] = a.array[index]
a.array = append(a.array[:index], a.array[index+1:]...)
}
return array
}
// PopLefts pops and returns <size> items from the beginning of array.
func (a *SortedStringArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0:size]
a.array = a.array[size:]
return value
}
// PopRights pops and returns <size> items from the end of array.
func (a *SortedStringArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index:]
a.array = a.array[:index]
return value
}
// 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 {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start : end]
}
return array
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
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])
} else {
array = a.array[start:end]
}
return array
}
// Sum returns the sum of values in an array.
func (a *SortedStringArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
}
// Len returns the length of array.
func (a *SortedStringArray) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
}
// Slice returns the underlying data of array.
// Notice, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
func (a *SortedStringArray) Slice() []string {
array := ([]string)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]string, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
array := ([]string)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]string, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
}
// Contains checks whether a value exists in the array.
func (a *SortedStringArray) Contains(value string) bool {
return a.Search(value) != -1
return a.Search(value) != -1
}
// Search searches array by <value>, returns the index of <value>,
@ -290,93 +290,95 @@ func (a *SortedStringArray) Search(value string) (index int) {
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
// If <result> greater than 0, it means the value at <index> is greater than <value>.
func (a *SortedStringArray) binSearch(value string, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
}
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
switch {
case cmp < 0 : max = mid - 1
case cmp > 0 : min = mid + 1
default :
return mid, cmp
}
}
return mid, cmp
if len(a.array) == 0 {
return -1, -2
}
if lock {
a.mu.RLock()
defer a.mu.RUnlock()
}
min := 0
max := len(a.array) - 1
mid := 0
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
case cmp > 0:
min = mid + 1
default:
return mid, cmp
}
}
return mid, cmp
}
// SetUnique sets unique mark to the array,
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedStringArray) SetUnique(unique bool) *SortedStringArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
if unique && oldUnique != unique {
a.Unique()
}
return a
oldUnique := a.unique.Val()
a.unique.Set(unique)
if unique && oldUnique != unique {
a.Unique()
}
return a
}
// Unique uniques the array, clear repeated items.
func (a *SortedStringArray) Unique() *SortedStringArray {
a.mu.Lock()
i := 0
for {
if i == len(a.array) - 1 {
break
}
if a.comparator(a.array[i], a.array[i + 1]) == 0 {
a.array = append(a.array[ : i + 1], a.array[i + 1 + 1 : ]...)
} else {
i++
}
}
a.mu.Unlock()
return a
a.mu.Lock()
i := 0
for {
if i == len(a.array)-1 {
break
}
if a.comparator(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
} else {
i++
}
}
a.mu.Unlock()
return a
}
// Clone returns a new array, which is a copy of current array.
func (a *SortedStringArray) Clone() (newArray *SortedStringArray) {
a.mu.RLock()
array := make([]string, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedStringArrayFrom(array, !a.mu.IsSafe())
a.mu.RLock()
array := make([]string, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedStringArrayFrom(array, !a.mu.IsSafe())
}
// Clear deletes all items of current array.
func (a *SortedStringArray) Clear() *SortedStringArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]string, 0)
}
a.mu.Unlock()
return a
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]string, 0)
}
a.mu.Unlock()
return a
}
// LockFunc locks writing by callback function <f>.
func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray {
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
a.mu.Lock()
defer a.mu.Unlock()
f(a.array)
return a
}
// RLockFunc locks reading by callback function <f>.
func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray {
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
a.mu.RLock()
defer a.mu.RUnlock()
f(a.array)
return a
}
// Merge merges <array> into current array.
@ -384,99 +386,105 @@ func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedStringArray) Merge(array interface{}) *SortedStringArray {
switch v := array.(type) {
case *Array: a.Add(gconv.Strings(v.Slice())...)
case *IntArray: a.Add(gconv.Strings(v.Slice())...)
case *StringArray: a.Add(gconv.Strings(v.Slice())...)
case *SortedArray: a.Add(gconv.Strings(v.Slice())...)
case *SortedIntArray: a.Add(gconv.Strings(v.Slice())...)
case *SortedStringArray: a.Add(gconv.Strings(v.Slice())...)
default:
a.Add(gconv.Strings(array)...)
}
return a
switch v := array.(type) {
case *Array:
a.Add(gconv.Strings(v.Slice())...)
case *IntArray:
a.Add(gconv.Strings(v.Slice())...)
case *StringArray:
a.Add(gconv.Strings(v.Slice())...)
case *SortedArray:
a.Add(gconv.Strings(v.Slice())...)
case *SortedIntArray:
a.Add(gconv.Strings(v.Slice())...)
case *SortedStringArray:
a.Add(gconv.Strings(v.Slice())...)
default:
a.Add(gconv.Strings(array)...)
}
return a
}
// Chunk splits an array into multiple arrays,
// the size of each array is determined by <size>.
// The last chunk may contain less than size elements.
func (a *SortedStringArray) Chunk(size int) [][]string {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]string
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]string
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size:end])
i++
}
return n
}
// SubSlice returns a slice of elements from the array as specified
// by the <offset> and <size> 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:]
}
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()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
a.mu.RLock()
defer a.mu.RUnlock()
return a.array[grand.Intn(len(a.array))]
}
// Rands randomly returns <size> items from array(no deleting).
func (a *SortedStringArray) Rands(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size-1 {
break
}
}
return n
}
// Join joins array elements with a string <glue>.
func (a *SortedStringArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array) - 1 {
buffer.WriteString(glue)
}
}
return buffer.String()
a.mu.RLock()
defer a.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
if k != len(a.array)-1 {
buffer.WriteString(glue)
}
}
return buffer.String()
}
// CountValues counts the number of occurrences of all values in the array.
@ -495,4 +503,4 @@ func (a *SortedStringArray) String() string {
a.mu.RLock()
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
}

View File

@ -7,105 +7,105 @@
package garray_test
import (
"fmt"
"github.com/gogf/gf/g/container/garray"
"fmt"
"github.com/gogf/gf/g/container/garray"
)
func Example_basic() {
// 创建普通的数组,默认并发安全(带锁)
a := garray.New()
// 创建普通的数组,默认并发安全(带锁)
a := garray.New()
// 添加数据项
for i := 0; i < 10; i++ {
a.Append(i)
}
// 添加数据项
for i := 0; i < 10; i++ {
a.Append(i)
}
// 获取当前数组长度
fmt.Println(a.Len())
// 获取当前数组长度
fmt.Println(a.Len())
// 获取当前数据项列表
fmt.Println(a.Slice())
// 获取当前数据项列表
fmt.Println(a.Slice())
// 获取指定索引项
fmt.Println(a.Get(6))
// 获取指定索引项
fmt.Println(a.Get(6))
// 查找指定数据项是否存在
fmt.Println(a.Contains(6))
fmt.Println(a.Contains(100))
// 查找指定数据项是否存在
fmt.Println(a.Contains(6))
fmt.Println(a.Contains(100))
// 在指定索引前插入数据项
a.InsertAfter(9, 11)
// 在指定索引后插入数据项
a.InsertBefore(10, 10)
// 在指定索引前插入数据项
a.InsertAfter(9, 11)
// 在指定索引后插入数据项
a.InsertBefore(10, 10)
fmt.Println(a.Slice())
fmt.Println(a.Slice())
// 修改指定索引的数据项
a.Set(0, 100)
fmt.Println(a.Slice())
// 修改指定索引的数据项
a.Set(0, 100)
fmt.Println(a.Slice())
// 搜索数据项,返回搜索到的索引位置
fmt.Println(a.Search(5))
// 搜索数据项,返回搜索到的索引位置
fmt.Println(a.Search(5))
// 删除指定索引的数据项
a.Remove(0)
fmt.Println(a.Slice())
// 删除指定索引的数据项
a.Remove(0)
fmt.Println(a.Slice())
// 清空数组
fmt.Println(a.Slice())
a.Clear()
fmt.Println(a.Slice())
// 清空数组
fmt.Println(a.Slice())
a.Clear()
fmt.Println(a.Slice())
// Output:
// 10
// [0 1 2 3 4 5 6 7 8 9]
// 6
// true
// false
// [0 1 2 3 4 5 6 7 8 9 10 11]
// [100 1 2 3 4 5 6 7 8 9 10 11]
// 5
// [1 2 3 4 5 6 7 8 9 10 11]
// [1 2 3 4 5 6 7 8 9 10 11]
// []
// Output:
// 10
// [0 1 2 3 4 5 6 7 8 9]
// 6
// true
// false
// [0 1 2 3 4 5 6 7 8 9 10 11]
// [100 1 2 3 4 5 6 7 8 9 10 11]
// 5
// [1 2 3 4 5 6 7 8 9 10 11]
// [1 2 3 4 5 6 7 8 9 10 11]
// []
}
func Example_rand() {
array := garray.NewFrom([]interface{}{1,2,3,4,5,6,7,8,9})
// 随机返回两个数据项(不删除)
fmt.Println(array.Rands(2))
fmt.Println(array.PopRand())
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// 随机返回两个数据项(不删除)
fmt.Println(array.Rands(2))
fmt.Println(array.PopRand())
}
func Example_pop() {
array := garray.NewFrom([]interface{}{1,2,3,4,5,6,7,8,9})
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
// Output:
// 1
// [2 3]
// 9
// [7 8]
// Output:
// 1
// [2 3]
// 9
// [7 8]
}
func Example_merge() {
array1 := garray.NewFrom([]interface{}{1,2})
array2 := garray.NewFrom([]interface{}{3,4})
slice1 := []interface{}{5,6}
slice2 := []int{7,8}
slice3 := []string{"9","0"}
fmt.Println(array1.Slice())
array1.Merge(array1)
array1.Merge(array2)
array1.Merge(slice1)
array1.Merge(slice2)
array1.Merge(slice3)
fmt.Println(array1.Slice())
array1 := garray.NewFrom([]interface{}{1, 2})
array2 := garray.NewFrom([]interface{}{3, 4})
slice1 := []interface{}{5, 6}
slice2 := []int{7, 8}
slice3 := []string{"9", "0"}
fmt.Println(array1.Slice())
array1.Merge(array1)
array1.Merge(array2)
array1.Merge(slice1)
array1.Merge(slice2)
array1.Merge(slice3)
fmt.Println(array1.Slice())
// Output:
// [1 2]
// [1 2 1 2 3 4 5 6 7 8 9 0]
}
// Output:
// [1 2]
// [1 2 1 2 3 4 5 6 7 8 9 0]
}

View File

@ -7,23 +7,23 @@
package gchan_test
import (
"fmt"
"github.com/gogf/gf/g/container/gchan"
"fmt"
"github.com/gogf/gf/g/container/gchan"
)
func Example_basic() {
n := 10
c := gchan.New(n)
for i := 0; i < n; i++ {
c.Push(i)
}
fmt.Println(c.Len(), c.Cap())
for i := 0; i < n; i++ {
fmt.Print(c.Pop())
}
c.Close()
n := 10
c := gchan.New(n)
for i := 0; i < n; i++ {
c.Push(i)
}
fmt.Println(c.Len(), c.Cap())
for i := 0; i < n; i++ {
fmt.Print(c.Pop())
}
c.Close()
// Output:
//10 10
//0123456789
// Output:
//10 10
//0123456789
}

View File

@ -9,115 +9,115 @@
package glist
import (
"container/list"
"github.com/gogf/gf/g/internal/rwmutex"
"container/list"
"github.com/gogf/gf/g/internal/rwmutex"
)
type (
List struct {
mu *rwmutex.RWMutex
list *list.List
mu *rwmutex.RWMutex
list *list.List
}
Element = list.Element
)
// New creates and returns a new empty doubly linked list.
func New(unsafe...bool) *List {
return &List {
mu : rwmutex.New(unsafe...),
list : list.New(),
}
func New(unsafe ...bool) *List {
return &List{
mu: rwmutex.New(unsafe...),
list: list.New(),
}
}
// PushFront inserts a new element <e> with value <v> at the front of list <l> and returns <e>.
func (l *List) PushFront(v interface{}) (e *Element) {
l.mu.Lock()
e = l.list.PushFront(v)
l.mu.Unlock()
return
l.mu.Lock()
e = l.list.PushFront(v)
l.mu.Unlock()
return
}
// PushBack inserts a new element <e> with value <v> at the back of list <l> and returns <e>.
func (l *List) PushBack(v interface{}) (e *Element) {
l.mu.Lock()
e = l.list.PushBack(v)
l.mu.Unlock()
return
l.mu.Lock()
e = l.list.PushBack(v)
l.mu.Unlock()
return
}
// PushFronts inserts multiple new elements with values <values> at the front of list <l>.
func (l *List) PushFronts(values []interface{}) {
l.mu.Lock()
l.mu.Lock()
for _, v := range values {
l.list.PushFront(v)
l.list.PushFront(v)
}
l.mu.Unlock()
l.mu.Unlock()
}
// PushBacks inserts multiple new elements with values <values> at the back of list <l>.
func (l *List) PushBacks(values []interface{}) {
l.mu.Lock()
for _, v := range values {
l.list.PushBack(v)
}
l.mu.Unlock()
l.mu.Lock()
for _, v := range values {
l.list.PushBack(v)
}
l.mu.Unlock()
}
// PopBack removes the element from back of <l> and returns the value of the element.
func (l *List) PopBack() (value interface{}) {
l.mu.Lock()
l.mu.Lock()
if e := l.list.Back(); e != nil {
value = l.list.Remove(e)
value = l.list.Remove(e)
}
l.mu.Unlock()
l.mu.Unlock()
return
}
// PopFront removes the element from front of <l> and returns the value of the element.
func (l *List) PopFront() (value interface{}) {
l.mu.Lock()
if e := l.list.Front(); e != nil {
value = l.list.Remove(e)
}
l.mu.Unlock()
return
l.mu.Lock()
if e := l.list.Front(); e != nil {
value = l.list.Remove(e)
}
l.mu.Unlock()
return
}
// PopBacks removes <max> elements from back of <l>
// and returns values of the removed elements as slice.
func (l *List) PopBacks(max int) (values []interface{}) {
l.mu.Lock()
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
length = max
}
values = make([]interface{}, length)
for i := 0; i < length; i++ {
values[i] = l.list.Remove(l.list.Back())
}
}
l.mu.Unlock()
return
l.mu.Lock()
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
length = max
}
values = make([]interface{}, length)
for i := 0; i < length; i++ {
values[i] = l.list.Remove(l.list.Back())
}
}
l.mu.Unlock()
return
}
// PopFronts removes <max> elements from front of <l>
// and returns values of the removed elements as slice.
func (l *List) PopFronts(max int) (values []interface{}) {
l.mu.Lock()
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
length = max
}
values = make([]interface{}, length)
for i := 0; i < length; i++ {
values[i] = l.list.Remove(l.list.Front())
}
}
l.mu.Unlock()
return
l.mu.Lock()
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
length = max
}
values = make([]interface{}, length)
for i := 0; i < length; i++ {
values[i] = l.list.Remove(l.list.Front())
}
}
l.mu.Unlock()
return
}
// PopBackAll removes all elements from back of <l>
@ -129,79 +129,79 @@ func (l *List) PopBackAll() []interface{} {
// PopFrontAll removes all elements from front of <l>
// and returns values of the removed elements as slice.
func (l *List) PopFrontAll() []interface{} {
return l.PopFronts(-1)
return l.PopFronts(-1)
}
// FrontAll copies and returns values of all elements from front of <l> as slice.
func (l *List) FrontAll() (values []interface{}) {
l.mu.RLock()
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
for i, e := 0, l.list.Front(); i < length; i, e = i + 1, e.Next() {
values[i] = e.Value
}
}
l.mu.RUnlock()
return
l.mu.RLock()
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
values[i] = e.Value
}
}
l.mu.RUnlock()
return
}
// BackAll copies and returns values of all elements from back of <l> as slice.
func (l *List) BackAll() (values []interface{}) {
l.mu.RLock()
l.mu.RLock()
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
for i, e := 0, l.list.Back(); i < length; i, e = i + 1, e.Prev() {
values[i] = e.Value
}
}
l.mu.RUnlock()
values = make([]interface{}, length)
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
values[i] = e.Value
}
}
l.mu.RUnlock()
return
}
// FrontValue returns value of the first element of <l> or nil if the list is empty.
func (l *List) FrontValue() (value interface{}) {
l.mu.RLock()
if e := l.list.Front(); e != nil {
value = e.Value
}
l.mu.RUnlock()
return
l.mu.RLock()
if e := l.list.Front(); e != nil {
value = e.Value
}
l.mu.RUnlock()
return
}
// BackValue returns value of the last element of <l> or nil if the list is empty.
func (l *List) BackValue() (value interface{}) {
l.mu.RLock()
if e := l.list.Back(); e != nil {
value = e.Value
}
l.mu.RUnlock()
return
l.mu.RLock()
if e := l.list.Back(); e != nil {
value = e.Value
}
l.mu.RUnlock()
return
}
// Front returns the first element of list <l> or nil if the list is empty.
func (l *List) Front() (e *Element) {
l.mu.RLock()
e = l.list.Front()
l.mu.RUnlock()
return
l.mu.RLock()
e = l.list.Front()
l.mu.RUnlock()
return
}
// Back returns the last element of list <l> or nil if the list is empty.
func (l *List) Back() (e *Element) {
l.mu.RLock()
e = l.list.Back()
l.mu.RUnlock()
return
l.mu.RLock()
e = l.list.Back()
l.mu.RUnlock()
return
}
// Len returns the number of elements of list <l>.
// The complexity is O(1).
func (l *List) Len() (length int) {
l.mu.RLock()
length = l.list.Len()
l.mu.RUnlock()
l.mu.RLock()
length = l.list.Len()
l.mu.RUnlock()
return
}
@ -214,107 +214,107 @@ func (l *List) Size() int {
// If <e> or <p> is not an element of <l>, or <e> == <p>, the list is not modified.
// The element and <p> must not be nil.
func (l *List) MoveBefore(e, p *Element) {
l.mu.Lock()
l.list.MoveBefore(e, p)
l.mu.Unlock()
l.mu.Lock()
l.list.MoveBefore(e, p)
l.mu.Unlock()
}
// MoveAfter moves element <e> to its new position after <p>.
// If <e> or <p> is not an element of <l>, or <e> == <p>, the list is not modified.
// The element and <p> must not be nil.
func (l *List) MoveAfter(e, p *Element) {
l.mu.Lock()
l.list.MoveAfter(e, p)
l.mu.Unlock()
l.mu.Lock()
l.list.MoveAfter(e, p)
l.mu.Unlock()
}
// MoveToFront moves element <e> to the front of list <l>.
// If <e> is not an element of <l>, the list is not modified.
// The element must not be nil.
func (l *List) MoveToFront(e *Element) {
l.mu.Lock()
l.list.MoveToFront(e)
l.mu.Unlock()
l.mu.Lock()
l.list.MoveToFront(e)
l.mu.Unlock()
}
// MoveToBack moves element <e> to the back of list <l>.
// If <e> is not an element of <l>, the list is not modified.
// The element must not be nil.
func (l *List) MoveToBack(e *Element) {
l.mu.Lock()
l.list.MoveToBack(e)
l.mu.Unlock()
l.mu.Lock()
l.list.MoveToBack(e)
l.mu.Unlock()
}
// PushBackList inserts a copy of an other list at the back of list <l>.
// The lists <l> and <other> may be the same, but they must not be nil.
func (l *List) PushBackList(other *List) {
if l != other {
other.mu.RLock()
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushBackList(other.list)
l.mu.Unlock()
if l != other {
other.mu.RLock()
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushBackList(other.list)
l.mu.Unlock()
}
// PushFrontList inserts a copy of an other list at the front of list <l>.
// The lists <l> and <other> may be the same, but they must not be nil.
func (l *List) PushFrontList(other *List) {
if l != other {
other.mu.RLock()
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushFrontList(other.list)
l.mu.Unlock()
if l != other {
other.mu.RLock()
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushFrontList(other.list)
l.mu.Unlock()
}
// InsertAfter inserts a new element <e> with value <v> immediately after <p> and returns <e>.
// If <p> is not an element of <l>, the list is not modified.
// The <p> must not be nil.
func (l *List) InsertAfter(v interface{}, p *Element) (e *Element) {
l.mu.Lock()
e = l.list.InsertAfter(v, p)
l.mu.Unlock()
return
l.mu.Lock()
e = l.list.InsertAfter(v, p)
l.mu.Unlock()
return
}
// InsertBefore inserts a new element <e> with value <v> immediately before <p> and returns <e>.
// If <p> is not an element of <l>, the list is not modified.
// The <p> must not be nil.
func (l *List) InsertBefore(v interface{}, p *Element) (e *Element) {
l.mu.Lock()
e = l.list.InsertBefore(v, p)
l.mu.Unlock()
return
l.mu.Lock()
e = l.list.InsertBefore(v, p)
l.mu.Unlock()
return
}
// Remove removes <e> from <l> if <e> is an element of list <l>.
// It returns the element value e.Value.
// The element must not be nil.
func (l *List) Remove(e *Element) (value interface{}) {
l.mu.Lock()
value = l.list.Remove(e)
l.mu.Unlock()
return
l.mu.Lock()
value = l.list.Remove(e)
l.mu.Unlock()
return
}
// Removes removes multiple elements <es> from <l> if <es> are elements of list <l>.
func (l *List) Removes(es []*Element) {
l.mu.Lock()
for _, e := range es {
l.list.Remove(e)
}
l.mu.Unlock()
return
l.mu.Lock()
for _, e := range es {
l.list.Remove(e)
}
l.mu.Unlock()
return
}
// RemoveAll removes all elements from list <l>.
func (l *List) RemoveAll() {
l.mu.Lock()
l.list = list.New()
l.mu.Unlock()
l.mu.Lock()
l.list = list.New()
l.mu.Unlock()
}
// See RemoveAll().
@ -324,30 +324,30 @@ func (l *List) Clear() {
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (l *List) RLockFunc(f func(list *list.List)) {
l.mu.RLock()
defer l.mu.RUnlock()
f(l.list)
l.mu.RLock()
defer l.mu.RUnlock()
f(l.list)
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (l *List) LockFunc(f func(list *list.List)) {
l.mu.Lock()
defer l.mu.Unlock()
f(l.list)
l.mu.Lock()
defer l.mu.Unlock()
f(l.list)
}
// Iterator is alias of IteratorAsc.
func (l *List) Iterator(f func (e *Element) bool) {
func (l *List) Iterator(f func(e *Element) bool) {
l.IteratorAsc(f)
}
// IteratorAsc iterates the list in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorAsc(f func (e *Element) bool) {
func (l *List) IteratorAsc(f func(e *Element) bool) {
l.mu.RLock()
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Front(); i < length; i, e = i + 1, e.Next() {
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
if !f(e) {
break
}
@ -358,15 +358,15 @@ func (l *List) IteratorAsc(f func (e *Element) bool) {
// IteratorDesc iterates the list in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorDesc(f func (e *Element) bool) {
func (l *List) IteratorDesc(f func(e *Element) bool) {
l.mu.RLock()
length := l.list.Len()
if length > 0 {
for i, e := 0, l.list.Back(); i < length; i, e = i + 1, e.Prev() {
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
if !f(e) {
break
}
}
}
l.mu.RUnlock()
}
}

View File

@ -7,30 +7,30 @@
package glist_test
import (
"fmt"
"github.com/gogf/gf/g/container/glist"
"fmt"
"github.com/gogf/gf/g/container/glist"
)
func Example_basic() {
n := 10
l := glist.New()
for i := 0; i < n; i++ {
l.PushBack(i)
}
fmt.Println(l.Len())
fmt.Println(l.FrontAll())
fmt.Println(l.BackAll())
for i := 0; i < n; i++ {
fmt.Print(l.PopFront())
}
l.Clear()
fmt.Println()
fmt.Println(l.Len())
n := 10
l := glist.New()
for i := 0; i < n; i++ {
l.PushBack(i)
}
fmt.Println(l.Len())
fmt.Println(l.FrontAll())
fmt.Println(l.BackAll())
for i := 0; i < n; i++ {
fmt.Print(l.PopFront())
}
l.Clear()
fmt.Println()
fmt.Println(l.Len())
// Output:
//10
//[0 1 2 3 4 5 6 7 8 9]
//[9 8 7 6 5 4 3 2 1 0]
//0123456789
//0
// Output:
//10
//[0 1 2 3 4 5 6 7 8 9]
//[9 8 7 6 5 4 3 2 1 0]
//0123456789
//0
}

View File

@ -9,48 +9,45 @@
package glist
import (
"testing"
"testing"
)
var (
l = New()
bn = 20000000
l = New()
bn = 20000000
)
func Benchmark_PushBack(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.PushBack(i)
}
b.N = bn
for i := 0; i < b.N; i++ {
l.PushBack(i)
}
}
func Benchmark_PushFront(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.PushFront(i)
}
b.N = bn
for i := 0; i < b.N; i++ {
l.PushFront(i)
}
}
func Benchmark_Len(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.Len()
}
b.N = bn
for i := 0; i < b.N; i++ {
l.Len()
}
}
func Benchmark_PopFront(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.PopFront()
}
b.N = bn
for i := 0; i < b.N; i++ {
l.PopFront()
}
}
func Benchmark_PopBack(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.PopBack()
}
b.N = bn
for i := 0; i < b.N; i++ {
l.PopBack()
}
}

View File

@ -8,7 +8,7 @@
package gmap
// Map based on hash table, alias of AnyAnyMap.
type Map = AnyAnyMap
type Map = AnyAnyMap
type HashMap = AnyAnyMap
// New returns an empty hash map.
@ -23,7 +23,7 @@ func New(unsafe ...bool) *Map {
// there might be some concurrent-safe issues when changing the map outside.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewFrom(data map[interface{}]interface{}, unsafe...bool) *Map {
func NewFrom(data map[interface{}]interface{}, unsafe ...bool) *Map {
return NewAnyAnyMapFrom(data, unsafe...)
}
@ -39,6 +39,6 @@ func NewHashMap(unsafe ...bool) *Map {
// there might be some concurrent-safe issues when changing the map outside.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewHashMapFrom(data map[interface{}]interface{}, unsafe...bool) *Map {
func NewHashMapFrom(data map[interface{}]interface{}, unsafe ...bool) *Map {
return NewAnyAnyMapFrom(data, unsafe...)
}
}

View File

@ -12,8 +12,8 @@ import (
)
type AnyAnyMap struct {
mu *rwmutex.RWMutex
data map[interface{}]interface{}
mu *rwmutex.RWMutex
data map[interface{}]interface{}
}
// NewAnyAnyMap returns an empty hash map.
@ -21,63 +21,63 @@ type AnyAnyMap struct {
// which is false in default, means concurrent-safe.
func NewAnyAnyMap(unsafe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu : rwmutex.New(unsafe...),
data : make(map[interface{}]interface{}),
mu: rwmutex.New(unsafe...),
data: make(map[interface{}]interface{}),
}
}
// NewAnyAnyMapFrom returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewAnyAnyMapFrom(data map[interface{}]interface{}, unsafe...bool) *AnyAnyMap {
return &AnyAnyMap{
mu : rwmutex.New(unsafe...),
data : data,
}
func NewAnyAnyMapFrom(data map[interface{}]interface{}, unsafe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu: rwmutex.New(unsafe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *AnyAnyMap) Iterator(f func (k interface{}, v interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
func (m *AnyAnyMap) Iterator(f func(k interface{}, v interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *AnyAnyMap) Clone(unsafe ...bool) *AnyAnyMap {
return NewFrom(m.Map(), unsafe ...)
return NewFrom(m.Map(), unsafe...)
}
// Map returns a copy of the data of the hash map.
func (m *AnyAnyMap) Map() map[interface{}]interface{} {
m.mu.RLock()
m.mu.RLock()
data := make(map[interface{}]interface{}, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
// Set sets key-value to the hash map.
func (m *AnyAnyMap) Set(key interface{}, val interface{}) {
m.mu.Lock()
m.data[key] = val
m.mu.Unlock()
m.mu.Lock()
m.data[key] = val
m.mu.Unlock()
}
// Sets batch sets key-values to the hash map.
func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
}
m.mu.Unlock()
m.mu.Lock()
for k, v := range data {
m.data[k] = v
}
m.mu.Unlock()
}
// Search searches the map with given <key>.
@ -91,10 +91,10 @@ func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
// Get returns the value by given <key>.
func (m *AnyAnyMap) Get(key interface{}) interface{} {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
}
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
@ -107,26 +107,26 @@ func (m *AnyAnyMap) Get(key interface{}) interface{} {
//
// It returns value with given <key>.
func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface {}); ok {
value = f()
}
m.data[key] = value
return value
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface{}); ok {
value = f()
}
m.data[key] = value
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
} else {
return v
}
return m.doSetWithLockCheck(key, value)
} else {
return v
}
}
// GetOrSetFunc returns the value by key,
@ -134,10 +134,10 @@ func (m *AnyAnyMap) GetOrSet(key interface{}, value interface{}) interface{} {
// and returns this value.
func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
} else {
return v
}
return m.doSetWithLockCheck(key, f())
} else {
return v
}
}
// GetOrSetFuncLock returns the value by key,
@ -148,10 +148,10 @@ func (m *AnyAnyMap) GetOrSetFunc(key interface{}, f func() interface{}) interfac
// with mutex.Lock of the hash map.
func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
return v
}
return m.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
@ -181,11 +181,11 @@ func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
@ -213,13 +213,13 @@ func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{})
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *AnyAnyMap) Remove(key interface{}) interface{} {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Removes batch deletes values of the map by keys.
@ -233,98 +233,98 @@ func (m *AnyAnyMap) Removes(keys []interface{}) {
// Keys returns all keys of the map as a slice.
func (m *AnyAnyMap) Keys() []interface{} {
m.mu.RLock()
keys := make([]interface{}, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
index++
}
m.mu.RUnlock()
return keys
m.mu.RLock()
keys := make([]interface{}, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
index++
}
m.mu.RUnlock()
return keys
}
// Values returns all values of the map as a slice.
func (m *AnyAnyMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, len(m.data))
index := 0
for _, value := range m.data {
values[index] = value
index++
}
m.mu.RUnlock()
return values
m.mu.RLock()
values := make([]interface{}, len(m.data))
index := 0
for _, value := range m.data {
values[index] = value
index++
}
m.mu.RUnlock()
return values
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *AnyAnyMap) Contains(key interface{}) bool {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (m *AnyAnyMap) Size() int {
m.mu.RLock()
length := len(m.data)
m.mu.RUnlock()
return length
m.mu.RLock()
length := len(m.data)
m.mu.RUnlock()
return length
}
// IsEmpty checks whether the map is empty.
// It returns true if map is empty, or else false.
func (m *AnyAnyMap) IsEmpty() bool {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *AnyAnyMap) Clear() {
m.mu.Lock()
m.data = make(map[interface{}]interface{})
m.mu.Unlock()
m.mu.Lock()
m.data = make(map[interface{}]interface{})
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (m *AnyAnyMap) RLockFunc(f func(m map[interface{}]interface{})) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
}
// Flip exchanges key-value of the map to value-key.
func (m *AnyAnyMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[interface{}]interface{}, len(m.data))
for k, v := range m.data {
n[v] = k
}
m.data = n
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[interface{}]interface{}, len(m.data))
for k, v := range m.data {
n[v] = k
}
m.data = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <m>.
func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -10,7 +10,7 @@ package gmap
import (
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/gconv"
)
type IntAnyMap struct {
@ -21,38 +21,38 @@ type IntAnyMap struct {
// NewIntAnyMap returns an empty IntAnyMap object.
// The parameter <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewIntAnyMap(unsafe...bool) *IntAnyMap {
func NewIntAnyMap(unsafe ...bool) *IntAnyMap {
return &IntAnyMap{
mu : rwmutex.New(unsafe...),
data : make(map[int]interface{}),
}
mu: rwmutex.New(unsafe...),
data: make(map[int]interface{}),
}
}
// NewIntAnyMapFrom returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewIntAnyMapFrom(data map[int]interface{}, unsafe...bool) *IntAnyMap {
return &IntAnyMap{
mu : rwmutex.New(unsafe...),
data : data,
}
func NewIntAnyMapFrom(data map[int]interface{}, unsafe ...bool) *IntAnyMap {
return &IntAnyMap{
mu: rwmutex.New(unsafe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *IntAnyMap) Iterator(f func (k int, v interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
func (m *IntAnyMap) Iterator(f func(k int, v interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *IntAnyMap) Clone() *IntAnyMap {
return NewIntAnyMapFrom(m.Map(), !m.mu.IsSafe())
return NewIntAnyMapFrom(m.Map(), !m.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
@ -62,7 +62,7 @@ func (m *IntAnyMap) Map() map[int]interface{} {
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
m.mu.RUnlock()
return data
}
@ -92,14 +92,13 @@ func (m *IntAnyMap) Search(key int) (value interface{}, found bool) {
}
// Get returns the value by given <key>.
func (m *IntAnyMap) Get(key int) (interface{}) {
func (m *IntAnyMap) Get(key int) interface{} {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
return val
}
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
// if not exists, set value to the map with given <key>,
// or else just return the existing value.
@ -110,38 +109,38 @@ func (m *IntAnyMap) Get(key int) (interface{}) {
//
// It returns value with given <key>.
func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface {}); ok {
value = f()
}
if value != nil {
m.data[key] = value
}
return value
m.mu.Lock()
defer m.mu.Unlock()
if v, ok := m.data[key]; ok {
return v
}
if f, ok := value.(func() interface{}); ok {
value = f()
}
if value != nil {
m.data[key] = value
}
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *IntAnyMap) GetOrSet(key int, value interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
} else {
return v
}
return m.doSetWithLockCheck(key, value)
} else {
return v
}
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist and returns this value.
func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
} else {
return v
}
return m.doSetWithLockCheck(key, f())
} else {
return v
}
}
// GetOrSetFuncLock returns the value by key,
@ -151,10 +150,10 @@ func (m *IntAnyMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
// with mutex.Lock of the hash map.
func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
return v
}
return m.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
@ -184,11 +183,11 @@ func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
@ -214,121 +213,120 @@ func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
return false
}
// Removes batch deletes values of the map by keys.
func (m *IntAnyMap) Removes(keys []int) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
}
m.mu.Unlock()
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *IntAnyMap) Remove(key int) interface{} {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Keys returns all keys of the map as a slice.
func (m *IntAnyMap) Keys() []int {
m.mu.RLock()
keys := make([]int, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
index++
}
m.mu.RUnlock()
return keys
m.mu.RLock()
keys := make([]int, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
index++
}
m.mu.RUnlock()
return keys
}
// Values returns all values of the map as a slice.
func (m *IntAnyMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, len(m.data))
index := 0
for _, value := range m.data {
values[index] = value
index++
}
m.mu.RUnlock()
return values
m.mu.RLock()
values := make([]interface{}, len(m.data))
index := 0
for _, value := range m.data {
values[index] = value
index++
}
m.mu.RUnlock()
return values
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *IntAnyMap) Contains(key int) bool {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (m *IntAnyMap) Size() int {
m.mu.RLock()
length := len(m.data)
m.mu.RUnlock()
return length
m.mu.RLock()
length := len(m.data)
m.mu.RUnlock()
return length
}
// IsEmpty checks whether the map is empty.
// It returns true if map is empty, or else false.
func (m *IntAnyMap) IsEmpty() bool {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *IntAnyMap) Clear() {
m.mu.Lock()
m.data = make(map[int]interface{})
m.mu.Unlock()
m.mu.Lock()
m.data = make(map[int]interface{})
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (m *IntAnyMap) RLockFunc(f func(m map[int]interface{})) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
}
// Flip exchanges key-value of the map to value-key.
func (m *IntAnyMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[int]interface{}, len(m.data))
for k, v := range m.data {
n[gconv.Int(v)] = k
}
m.data = n
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[int]interface{}, len(m.data))
for k, v := range m.data {
n[gconv.Int(v)] = k
}
m.data = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <m>.
func (m *IntAnyMap) Merge(other *IntAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -7,7 +7,7 @@
package gmap
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
)
type IntIntMap struct {
@ -18,38 +18,38 @@ type IntIntMap struct {
// NewIntIntMap returns an empty IntIntMap object.
// The parameter <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewIntIntMap(unsafe...bool) *IntIntMap {
func NewIntIntMap(unsafe ...bool) *IntIntMap {
return &IntIntMap{
mu : rwmutex.New(unsafe...),
data : make(map[int]int),
}
mu: rwmutex.New(unsafe...),
data: make(map[int]int),
}
}
// NewIntIntMapFrom returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewIntIntMapFrom(data map[int]int, unsafe...bool) *IntIntMap {
return &IntIntMap{
mu : rwmutex.New(unsafe...),
data : data,
}
func NewIntIntMapFrom(data map[int]int, unsafe ...bool) *IntIntMap {
return &IntIntMap{
mu: rwmutex.New(unsafe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *IntIntMap) Iterator(f func (k int, v int) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
func (m *IntIntMap) Iterator(f func(k int, v int) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
if !f(k, v) {
break
}
}
}
// Clone returns a new hash map with copy of current map data.
func (m *IntIntMap) Clone() *IntIntMap {
return NewIntIntMapFrom(m.Map(), !m.mu.IsSafe())
return NewIntIntMapFrom(m.Map(), !m.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
@ -59,7 +59,7 @@ func (m *IntIntMap) Map() map[int]int {
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
m.mu.RUnlock()
return data
}
@ -89,7 +89,7 @@ func (m *IntIntMap) Search(key int) (value int, found bool) {
}
// Get returns the value by given <key>.
func (m *IntIntMap) Get(key int) (int) {
func (m *IntIntMap) Get(key int) int {
m.mu.RLock()
val, _ := m.data[key]
m.mu.RUnlock()
@ -102,34 +102,34 @@ func (m *IntIntMap) Get(key int) (int) {
//
// It returns value with given <key>.
func (m *IntIntMap) doSetWithLockCheck(key int, value int) int {
m.mu.Lock()
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
m.mu.Lock()
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *IntIntMap) GetOrSet(key int, value int) int {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
} else {
return v
}
return m.doSetWithLockCheck(key, value)
} else {
return v
}
}
// GetOrSetFunc returns the value by key,
// or sets value with return value of callback function <f> if not exist and returns this value.
func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
} else {
return v
}
return m.doSetWithLockCheck(key, f())
} else {
return v
}
}
// GetOrSetFuncLock returns the value by key,
@ -139,27 +139,27 @@ func (m *IntIntMap) GetOrSetFunc(key int, f func() int) int {
// with mutex.Lock of the hash map.
func (m *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
if v, ok := m.Search(key); !ok {
m.mu.Lock()
defer m.mu.Unlock()
if v, ok = m.data[key]; ok {
return v
}
v = f()
m.data[key] = v
return v
} else {
return v
}
m.mu.Lock()
defer m.mu.Unlock()
if v, ok = m.data[key]; ok {
return v
}
v = f()
m.data[key] = v
return v
} else {
return v
}
}
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *IntIntMap) SetIfNotExist(key int, value int) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
@ -191,118 +191,118 @@ func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
// Removes batch deletes values of the map by keys.
func (m *IntIntMap) Removes(keys []int) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
}
m.mu.Unlock()
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *IntIntMap) Remove(key int) int {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
}
m.mu.Unlock()
return val
}
// Keys returns all keys of the map as a slice.
func (m *IntIntMap) Keys() []int {
m.mu.RLock()
keys := make([]int, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
index++
}
m.mu.RUnlock()
return keys
m.mu.RLock()
keys := make([]int, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
index++
}
m.mu.RUnlock()
return keys
}
// Values returns all values of the map as a slice.
func (m *IntIntMap) Values() []int {
m.mu.RLock()
values := make([]int, len(m.data))
index := 0
for _, value := range m.data {
values[index] = value
index++
}
m.mu.RUnlock()
return values
m.mu.RLock()
values := make([]int, len(m.data))
index := 0
for _, value := range m.data {
values[index] = value
index++
}
m.mu.RUnlock()
return values
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *IntIntMap) Contains(key int) bool {
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
m.mu.RLock()
_, exists := m.data[key]
m.mu.RUnlock()
return exists
}
// Size returns the size of the map.
func (m *IntIntMap) Size() int {
m.mu.RLock()
length := len(m.data)
m.mu.RUnlock()
return length
m.mu.RLock()
length := len(m.data)
m.mu.RUnlock()
return length
}
// IsEmpty checks whether the map is empty.
// It returns true if map is empty, or else false.
func (m *IntIntMap) IsEmpty() bool {
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
m.mu.RLock()
empty := len(m.data) == 0
m.mu.RUnlock()
return empty
}
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *IntIntMap) Clear() {
m.mu.Lock()
m.data = make(map[int]int)
m.mu.Unlock()
m.mu.Lock()
m.data = make(map[int]int)
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (m *IntIntMap) LockFunc(f func(m map[int]int)) {
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
m.mu.Lock()
defer m.mu.Unlock()
f(m.data)
}
// RLockFunc locks reading with given callback function <f> within RWMutex.RLock.
func (m *IntIntMap) RLockFunc(f func(m map[int]int)) {
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
m.mu.RLock()
defer m.mu.RUnlock()
f(m.data)
}
// Flip exchanges key-value of the map to value-key.
func (m *IntIntMap) Flip() {
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[int]int, len(m.data))
for k, v := range m.data {
n[v] = k
}
m.data = n
m.mu.Lock()
defer m.mu.Unlock()
n := make(map[int]int, len(m.data))
for k, v := range m.data {
n[v] = k
}
m.data = n
}
// Merge merges two hash maps.
// The <other> map will be merged into the map <m>.
func (m *IntIntMap) Merge(other *IntIntMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
for k, v := range other.data {
m.data[k] = v
}
}

View File

@ -21,8 +21,8 @@ type IntStrMap struct {
// which is false in default, means concurrent-safe.
func NewIntStrMap(unsafe ...bool) *IntStrMap {
return &IntStrMap{
mu : rwmutex.New(unsafe...),
data : make(map[int]string),
mu: rwmutex.New(unsafe...),
data: make(map[int]string),
}
}
@ -31,8 +31,8 @@ func NewIntStrMap(unsafe ...bool) *IntStrMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewIntStrMapFrom(data map[int]string, unsafe ...bool) *IntStrMap {
return &IntStrMap{
mu : rwmutex.New(unsafe...),
data : data,
mu: rwmutex.New(unsafe...),
data: data,
}
}
@ -213,7 +213,7 @@ func (m *IntStrMap) Remove(key int) string {
// Keys returns all keys of the map as a slice.
func (m *IntStrMap) Keys() []int {
m.mu.RLock()
keys := make([]int, len(m.data))
keys := make([]int, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
@ -227,7 +227,7 @@ func (m *IntStrMap) Keys() []int {
func (m *IntStrMap) Values() []string {
m.mu.RLock()
values := make([]string, len(m.data))
index := 0
index := 0
for _, value := range m.data {
values[index] = value
index++

View File

@ -23,8 +23,8 @@ type StrAnyMap struct {
// which is false in default, means concurrent-safe.
func NewStrAnyMap(unsafe ...bool) *StrAnyMap {
return &StrAnyMap{
mu : rwmutex.New(unsafe...),
data : make(map[string]interface{}),
mu: rwmutex.New(unsafe...),
data: make(map[string]interface{}),
}
}
@ -33,8 +33,8 @@ func NewStrAnyMap(unsafe ...bool) *StrAnyMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewStrAnyMapFrom(data map[string]interface{}, unsafe ...bool) *StrAnyMap {
return &StrAnyMap{
mu : rwmutex.New(unsafe...),
data : data,
mu: rwmutex.New(unsafe...),
data: data,
}
}
@ -238,7 +238,7 @@ func (m *StrAnyMap) Remove(key string) interface{} {
// Keys returns all keys of the map as a slice.
func (m *StrAnyMap) Keys() []string {
m.mu.RLock()
keys := make([]string, len(m.data))
keys := make([]string, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
@ -252,7 +252,7 @@ func (m *StrAnyMap) Keys() []string {
func (m *StrAnyMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, len(m.data))
index := 0
index := 0
for _, value := range m.data {
values[index] = value
index++

View File

@ -22,8 +22,8 @@ type StrIntMap struct {
// which is false in default, means concurrent-safe.
func NewStrIntMap(unsafe ...bool) *StrIntMap {
return &StrIntMap{
mu : rwmutex.New(unsafe...),
data : make(map[string]int),
mu: rwmutex.New(unsafe...),
data: make(map[string]int),
}
}
@ -32,8 +32,8 @@ func NewStrIntMap(unsafe ...bool) *StrIntMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewStrIntMapFrom(data map[string]int, unsafe ...bool) *StrIntMap {
return &StrIntMap{
mu : rwmutex.New(unsafe...),
data : data,
mu: rwmutex.New(unsafe...),
data: data,
}
}
@ -216,7 +216,7 @@ func (m *StrIntMap) Remove(key string) int {
// Keys returns all keys of the map as a slice.
func (m *StrIntMap) Keys() []string {
m.mu.RLock()
keys := make([]string, len(m.data))
keys := make([]string, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
@ -230,7 +230,7 @@ func (m *StrIntMap) Keys() []string {
func (m *StrIntMap) Values() []int {
m.mu.RLock()
values := make([]int, len(m.data))
index := 0
index := 0
for _, value := range m.data {
values[index] = value
index++

View File

@ -8,7 +8,7 @@
package gmap
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
)
type StrStrMap struct {
@ -19,26 +19,26 @@ type StrStrMap struct {
// NewStrStrMap returns an empty StrStrMap object.
// The parameter <unsafe> used to specify whether using map in un-concurrent-safety,
// which is false in default, means concurrent-safe.
func NewStrStrMap(unsafe...bool) *StrStrMap {
func NewStrStrMap(unsafe ...bool) *StrStrMap {
return &StrStrMap{
data : make(map[string]string),
mu : rwmutex.New(unsafe...),
data: make(map[string]string),
mu: rwmutex.New(unsafe...),
}
}
// NewStrStrMapFrom returns a hash map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewStrStrMapFrom(data map[string]string, unsafe...bool) *StrStrMap {
return &StrStrMap{
mu : rwmutex.New(unsafe...),
data : data,
}
func NewStrStrMapFrom(data map[string]string, unsafe ...bool) *StrStrMap {
return &StrStrMap{
mu: rwmutex.New(unsafe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *StrStrMap) Iterator(f func (k string, v string) bool) {
func (m *StrStrMap) Iterator(f func(k string, v string) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
for k, v := range m.data {
@ -50,18 +50,18 @@ func (m *StrStrMap) Iterator(f func (k string, v string) bool) {
// Clone returns a new hash map with copy of current map data.
func (m *StrStrMap) Clone() *StrStrMap {
return NewStrStrMapFrom(m.Map(), !m.mu.IsSafe())
return NewStrStrMapFrom(m.Map(), !m.mu.IsSafe())
}
// Map returns a copy of the data of the hash map.
func (m *StrStrMap) Map() map[string]string {
m.mu.RLock()
m.mu.RLock()
data := make(map[string]string, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
// Set sets key-value to the hash map.
@ -73,11 +73,11 @@ func (m *StrStrMap) Set(key string, val string) {
// Sets batch sets key-values to the hash map.
func (m *StrStrMap) Sets(data map[string]string) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
}
m.mu.Unlock()
m.mu.Lock()
for k, v := range data {
m.data[k] = v
}
m.mu.Unlock()
}
// Search searches the map with given <key>.
@ -194,11 +194,11 @@ func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
// Removes batch deletes values of the map by keys.
func (m *StrStrMap) Removes(keys []string) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
}
m.mu.Unlock()
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
@ -215,13 +215,13 @@ func (m *StrStrMap) Remove(key string) string {
// Keys returns all keys of the map as a slice.
func (m *StrStrMap) Keys() []string {
m.mu.RLock()
keys := make([]string, len(m.data))
keys := make([]string, len(m.data))
index := 0
for key := range m.data {
keys[index] = key
index++
}
m.mu.RUnlock()
m.mu.RUnlock()
return keys
}
@ -229,7 +229,7 @@ func (m *StrStrMap) Keys() []string {
func (m *StrStrMap) Values() []string {
m.mu.RLock()
values := make([]string, len(m.data))
index := 0
index := 0
for _, value := range m.data {
values[index] = value
index++
@ -266,9 +266,9 @@ func (m *StrStrMap) IsEmpty() bool {
// Clear deletes all data of the map, it will remake a new underlying data map.
func (m *StrStrMap) Clear() {
m.mu.Lock()
m.data = make(map[string]string)
m.mu.Unlock()
m.mu.Lock()
m.data = make(map[string]string)
m.mu.Unlock()
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.

View File

@ -13,14 +13,14 @@ import (
)
type ListMap struct {
mu *rwmutex.RWMutex
data map[interface{}]*glist.Element
list *glist.List
mu *rwmutex.RWMutex
data map[interface{}]*glist.Element
list *glist.List
}
type gListMapNode struct {
key interface{}
value interface{}
key interface{}
value interface{}
}
// NewListMap returns an empty link map.
@ -29,41 +29,41 @@ type gListMapNode struct {
// which is false in default, means concurrent-safe.
func NewListMap(unsafe ...bool) *ListMap {
return &ListMap{
mu : rwmutex.New(unsafe...),
data : make(map[interface{}]*glist.Element),
list : glist.New(true),
mu: rwmutex.New(unsafe...),
data: make(map[interface{}]*glist.Element),
list: glist.New(true),
}
}
// NewListMapFrom returns a link map from given map <data>.
// Note that, the param <data> map will be set as the underlying data map(no deep copy),
// there might be some concurrent-safe issues when changing the map outside.
func NewListMapFrom(data map[interface{}]interface{}, unsafe...bool) *ListMap {
m := NewListMap(unsafe...)
m.Sets(data)
return m
func NewListMapFrom(data map[interface{}]interface{}, unsafe ...bool) *ListMap {
m := NewListMap(unsafe...)
m.Sets(data)
return m
}
// Iterator is alias of IteratorAsc.
func (m *ListMap) Iterator(f func (key, value interface{}) bool) {
func (m *ListMap) Iterator(f func(key, value interface{}) bool) {
m.IteratorAsc(f)
}
// IteratorAsc iterates the map in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *ListMap) IteratorAsc(f func (key interface{}, value interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
func (m *ListMap) IteratorAsc(f func(key interface{}, value interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
}
// IteratorDesc iterates the map in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *ListMap) IteratorDesc(f func (key interface{}, value interface{}) bool) {
func (m *ListMap) IteratorDesc(f func(key interface{}, value interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
node := (*gListMapNode)(nil)
@ -75,7 +75,7 @@ func (m *ListMap) IteratorDesc(f func (key interface{}, value interface{}) bool)
// Clone returns a new link map with copy of current map data.
func (m *ListMap) Clone(unsafe ...bool) *ListMap {
return NewListMapFrom(m.Map(), unsafe ...)
return NewListMapFrom(m.Map(), unsafe...)
}
// Clear deletes all data of the map, it will remake a new underlying data map.
@ -88,7 +88,7 @@ func (m *ListMap) Clear() {
// Map returns a copy of the data of the map.
func (m *ListMap) Map() map[interface{}]interface{} {
m.mu.RLock()
m.mu.RLock()
node := (*gListMapNode)(nil)
data := make(map[interface{}]interface{}, len(m.data))
m.list.IteratorAsc(func(e *glist.Element) bool {
@ -96,32 +96,32 @@ func (m *ListMap) Map() map[interface{}]interface{} {
data[node.key] = node.value
return true
})
m.mu.RUnlock()
return data
m.mu.RUnlock()
return data
}
// Set sets key-value to the map.
func (m *ListMap) Set(key interface{}, value interface{}) {
m.mu.Lock()
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
e.Value = &gListMapNode{key, value}
}
m.mu.Unlock()
m.mu.Lock()
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
e.Value = &gListMapNode{key, value}
}
m.mu.Unlock()
}
// Sets batch sets key-values to the map.
func (m *ListMap) Sets(data map[interface{}]interface{}) {
m.mu.Lock()
for key, value := range data {
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
e.Value = &gListMapNode{key, value}
}
}
m.mu.Unlock()
m.mu.Lock()
for key, value := range data {
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
e.Value = &gListMapNode{key, value}
}
}
m.mu.Unlock()
}
// Search searches the map with given <key>.
@ -138,12 +138,12 @@ func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
// Get returns the value by given <key>.
func (m *ListMap) Get(key interface{}) (value interface{}) {
m.mu.RLock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
}
m.mu.RUnlock()
return
m.mu.RLock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
}
m.mu.RUnlock()
return
}
// doSetWithLockCheck checks whether value of the key exists with mutex.Lock,
@ -156,26 +156,26 @@ func (m *ListMap) Get(key interface{}) (value interface{}) {
//
// It returns value with given <key>.
func (m *ListMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if e, ok := m.data[key]; ok {
return e.Value.(*gListMapNode).value
}
if f, ok := value.(func() interface {}); ok {
value = f()
}
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
return value
m.mu.Lock()
defer m.mu.Unlock()
if e, ok := m.data[key]; ok {
return e.Value.(*gListMapNode).value
}
if f, ok := value.(func() interface{}); ok {
value = f()
}
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
return value
}
// GetOrSet returns the value by key,
// or set value with given <value> if not exist and returns this value.
func (m *ListMap) GetOrSet(key interface{}, value interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, value)
} else {
return v
}
return m.doSetWithLockCheck(key, value)
} else {
return v
}
}
// GetOrSetFunc returns the value by key,
@ -183,10 +183,10 @@ func (m *ListMap) GetOrSet(key interface{}, value interface{}) interface{} {
// and returns this value.
func (m *ListMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f())
} else {
return v
}
return m.doSetWithLockCheck(key, f())
} else {
return v
}
}
// GetOrSetFuncLock returns the value by key,
@ -197,10 +197,10 @@ func (m *ListMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{
// with mutex.Lock of the map.
func (m *ListMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
if v, ok := m.Search(key); !ok {
return m.doSetWithLockCheck(key, f)
} else {
return v
}
return m.doSetWithLockCheck(key, f)
} else {
return v
}
}
// GetVar returns a gvar.Var with the value by given <key>.
@ -230,11 +230,11 @@ func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gv
// SetIfNotExist sets <value> to the map if the <key> does not exist, then return true.
// It returns false if <key> exists, and <value> would be ignored.
func (m *ListMap) SetIfNotExist(key interface{}, value interface{}) bool {
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
if !m.Contains(key) {
m.doSetWithLockCheck(key, value)
return true
}
return false
}
// SetIfNotExistFunc sets value with return value of callback function <f>, then return true.
@ -262,14 +262,14 @@ func (m *ListMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) b
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *ListMap) Remove(key interface{}) (value interface{}) {
m.mu.Lock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
delete(m.data, key)
m.list.Remove(e)
}
m.mu.Unlock()
return
m.mu.Lock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
delete(m.data, key)
m.list.Remove(e)
}
m.mu.Unlock()
return
}
// Removes batch deletes values of the map by keys.
@ -286,81 +286,81 @@ func (m *ListMap) Removes(keys []interface{}) {
// Keys returns all keys of the map as a slice in ascending order.
func (m *ListMap) Keys() []interface{} {
m.mu.RLock()
keys := make([]interface{}, m.list.Len())
index := 0
m.list.IteratorAsc(func(e *glist.Element) bool {
keys[index] = e.Value.(*gListMapNode).key
index++
return true
})
m.mu.RUnlock()
return keys
m.mu.RLock()
keys := make([]interface{}, m.list.Len())
index := 0
m.list.IteratorAsc(func(e *glist.Element) bool {
keys[index] = e.Value.(*gListMapNode).key
index++
return true
})
m.mu.RUnlock()
return keys
}
// Values returns all values of the map as a slice.
func (m *ListMap) Values() []interface{} {
m.mu.RLock()
values := make([]interface{}, m.list.Len())
index := 0
m.mu.RLock()
values := make([]interface{}, m.list.Len())
index := 0
m.list.IteratorAsc(func(e *glist.Element) bool {
values[index] = e.Value.(*gListMapNode).value
index++
return true
})
m.mu.RUnlock()
return values
m.mu.RUnlock()
return values
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *ListMap) Contains(key interface{}) (ok bool) {
m.mu.RLock()
_, ok = m.data[key]
m.mu.RUnlock()
return
m.mu.RLock()
_, ok = m.data[key]
m.mu.RUnlock()
return
}
// Size returns the size of the map.
func (m *ListMap) Size() (size int) {
m.mu.RLock()
size = len(m.data)
m.mu.RUnlock()
return
m.mu.RLock()
size = len(m.data)
m.mu.RUnlock()
return
}
// IsEmpty checks whether the map is empty.
// It returns true if map is empty, or else false.
func (m *ListMap) IsEmpty() bool {
return m.Size() == 0
return m.Size() == 0
}
// Flip exchanges key-value of the map to value-key.
func (m *ListMap) Flip() {
data := m.Map()
m.Clear()
for key, value := range data {
m.Set(value, key)
}
data := m.Map()
m.Clear()
for key, value := range data {
m.Set(value, key)
}
}
// Merge merges two link maps.
// The <other> map will be merged into the map <m>.
func (m *ListMap) Merge(other *ListMap) {
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
m.mu.Lock()
defer m.mu.Unlock()
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
}
node := (*gListMapNode)(nil)
other.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
if e, ok := m.data[node.key]; !ok {
m.data[node.key] = m.list.PushBack(&gListMapNode{node.key, node.value})
} else {
e.Value = &gListMapNode{node.key, node.value}
}
return true
})
}
other.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
if e, ok := m.data[node.key]; !ok {
m.data[node.key] = m.list.PushBack(&gListMapNode{node.key, node.value})
} else {
e.Value = &gListMapNode{node.key, node.value}
}
return true
})
}

View File

@ -16,7 +16,7 @@ type TreeMap = gtree.RedBlackTree
// NewTreeMap instantiates a tree map with the custom comparator.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewTreeMap(comparator func(v1, v2 interface{}) int, unsafe...bool) *TreeMap {
func NewTreeMap(comparator func(v1, v2 interface{}) int, unsafe ...bool) *TreeMap {
return gtree.NewRedBlackTree(comparator, unsafe...)
}
@ -25,6 +25,6 @@ func NewTreeMap(comparator func(v1, v2 interface{}) int, unsafe...bool) *TreeMap
// there might be some concurrent-safe issues when changing the map outside.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewTreeMapFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *TreeMap {
func NewTreeMapFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe ...bool) *TreeMap {
return gtree.NewRedBlackTreeFrom(comparator, data, unsafe...)
}
}

View File

@ -68,8 +68,8 @@ func Test_Map_Batch(t *testing.T) {
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_Map_Iterator(t *testing.T){
expect :=map[interface{}]interface{}{1: 1, "key1": "val1"}
func Test_Map_Iterator(t *testing.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
@ -91,8 +91,8 @@ func Test_Map_Iterator(t *testing.T){
gtest.Assert(j, 1)
}
func Test_Map_Lock(t *testing.T){
expect :=map[interface{}]interface{}{1: 1, "key1": "val1"}
func Test_Map_Lock(t *testing.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewFrom(expect)
m.LockFunc(func(m map[interface{}]interface{}) {

View File

@ -19,9 +19,9 @@ var listMap = gmap.NewListMap()
var treeMap = gmap.NewTreeMap(gutil.ComparatorInt)
func Benchmark_HashMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
hashMap.Set(i, i)
}
for i := 0; i < b.N; i++ {
hashMap.Set(i, i)
}
}
func Benchmark_ListMap_Set(b *testing.B) {
@ -37,9 +37,9 @@ func Benchmark_TreeMap_Set(b *testing.B) {
}
func Benchmark_HashMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
hashMap.Get(i)
}
for i := 0; i < b.N; i++ {
hashMap.Get(i)
}
}
func Benchmark_ListMap_Get(b *testing.B) {

View File

@ -10,101 +10,98 @@ package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"strconv"
"testing"
"strconv"
)
var ififm = gmap.New()
var iim = gmap.NewIntIntMap()
var iifm = gmap.NewIntAnyMap()
var ism = gmap.NewIntStrMap()
var sim = gmap.NewStrIntMap()
var sifm = gmap.NewStrAnyMap()
var ssm = gmap.NewStrStrMap()
var iim = gmap.NewIntIntMap()
var iifm = gmap.NewIntAnyMap()
var ism = gmap.NewIntStrMap()
var sim = gmap.NewStrIntMap()
var sifm = gmap.NewStrAnyMap()
var ssm = gmap.NewStrStrMap()
func Benchmark_IntIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
iim.Set(i, i)
}
for i := 0; i < b.N; i++ {
iim.Set(i, i)
}
}
func Benchmark_IntAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
iifm.Set(i, i)
}
for i := 0; i < b.N; i++ {
iifm.Set(i, i)
}
}
func Benchmark_IntStrMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ism.Set(i, strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
ism.Set(i, strconv.Itoa(i))
}
}
func Benchmark_AnyAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ififm.Set(i, i)
}
for i := 0; i < b.N; i++ {
ififm.Set(i, i)
}
}
func Benchmark_StrIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sim.Set(strconv.Itoa(i), i)
}
for i := 0; i < b.N; i++ {
sim.Set(strconv.Itoa(i), i)
}
}
func Benchmark_StrAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sifm.Set(strconv.Itoa(i), i)
}
for i := 0; i < b.N; i++ {
sifm.Set(strconv.Itoa(i), i)
}
}
func Benchmark_StrStrMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ssm.Set(strconv.Itoa(i), strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
ssm.Set(strconv.Itoa(i), strconv.Itoa(i))
}
}
func Benchmark_IntIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
iim.Get(i)
}
for i := 0; i < b.N; i++ {
iim.Get(i)
}
}
func Benchmark_IntAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
iifm.Get(i)
}
for i := 0; i < b.N; i++ {
iifm.Get(i)
}
}
func Benchmark_IntStrMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ism.Get(i)
}
for i := 0; i < b.N; i++ {
ism.Get(i)
}
}
func Benchmark_AnyAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ififm.Get(i)
}
for i := 0; i < b.N; i++ {
ififm.Get(i)
}
}
func Benchmark_StrIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sim.Get(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
sim.Get(strconv.Itoa(i))
}
}
func Benchmark_StrAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sifm.Get(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
sifm.Get(strconv.Itoa(i))
}
}
func Benchmark_StrStrMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ssm.Get(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
ssm.Get(strconv.Itoa(i))
}
}

View File

@ -9,48 +9,46 @@
package gmap_test
import (
"testing"
"github.com/gogf/gf/g/container/gmap"
"sync"
"github.com/gogf/gf/g/container/gmap"
"sync"
"testing"
)
var m1 = gmap.NewIntIntMap()
var m2 = sync.Map{}
func BenchmarkGmapSet(b *testing.B) {
for i := 0; i < b.N; i++ {
m1.Set(i, i)
}
for i := 0; i < b.N; i++ {
m1.Set(i, i)
}
}
func BenchmarkSyncmapSet(b *testing.B) {
for i := 0; i < b.N; i++ {
m2.Store(i, i)
}
for i := 0; i < b.N; i++ {
m2.Store(i, i)
}
}
func BenchmarkGmapGet(b *testing.B) {
for i := 0; i < b.N; i++ {
m1.Get(i)
}
for i := 0; i < b.N; i++ {
m1.Get(i)
}
}
func BenchmarkSyncmapGet(b *testing.B) {
for i := 0; i < b.N; i++ {
m2.Load(i)
}
for i := 0; i < b.N; i++ {
m2.Load(i)
}
}
func BenchmarkGmapRemove(b *testing.B) {
for i := 0; i < b.N; i++ {
m1.Remove(i)
}
for i := 0; i < b.N; i++ {
m1.Remove(i)
}
}
func BenchmarkSyncmapRmove(b *testing.B) {
for i := 0; i < b.N; i++ {
m2.Delete(i)
}
for i := 0; i < b.N; i++ {
m2.Delete(i)
}
}

View File

@ -10,105 +10,102 @@ package gmap_test
import (
"github.com/gogf/gf/g/container/gmap"
"strconv"
"testing"
"strconv"
)
var ififmUnsafe = gmap.New(true)
var iimUnsafe = gmap.NewIntIntMap(true)
var iifmUnsafe = gmap.NewIntAnyMap(true)
var ismUnsafe = gmap.NewIntStrMap(true)
var simUnsafe = gmap.NewStrIntMap(true)
var sifmUnsafe = gmap.NewStrAnyMap(true)
var ssmUnsafe = gmap.NewStrStrMap(true)
var iimUnsafe = gmap.NewIntIntMap(true)
var iifmUnsafe = gmap.NewIntAnyMap(true)
var ismUnsafe = gmap.NewIntStrMap(true)
var simUnsafe = gmap.NewStrIntMap(true)
var sifmUnsafe = gmap.NewStrAnyMap(true)
var ssmUnsafe = gmap.NewStrStrMap(true)
// 写入性能测试
func Benchmark_Unsafe_IntIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
iimUnsafe.Set(i, i)
}
for i := 0; i < b.N; i++ {
iimUnsafe.Set(i, i)
}
}
func Benchmark_Unsafe_IntAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
iifmUnsafe.Set(i, i)
}
for i := 0; i < b.N; i++ {
iifmUnsafe.Set(i, i)
}
}
func Benchmark_Unsafe_IntStrMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ismUnsafe.Set(i, strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
ismUnsafe.Set(i, strconv.Itoa(i))
}
}
func Benchmark_Unsafe_AnyAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ififmUnsafe.Set(i, i)
}
for i := 0; i < b.N; i++ {
ififmUnsafe.Set(i, i)
}
}
func Benchmark_Unsafe_StrIntMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
simUnsafe.Set(strconv.Itoa(i), i)
}
for i := 0; i < b.N; i++ {
simUnsafe.Set(strconv.Itoa(i), i)
}
}
func Benchmark_Unsafe_StrAnyMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
sifmUnsafe.Set(strconv.Itoa(i), i)
}
for i := 0; i < b.N; i++ {
sifmUnsafe.Set(strconv.Itoa(i), i)
}
}
func Benchmark_Unsafe_StrStrMap_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
ssmUnsafe.Set(strconv.Itoa(i), strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
ssmUnsafe.Set(strconv.Itoa(i), strconv.Itoa(i))
}
}
// 读取性能测试
func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
iimUnsafe.Get(i)
}
for i := 0; i < b.N; i++ {
iimUnsafe.Get(i)
}
}
func Benchmark_Unsafe_IntAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
iifmUnsafe.Get(i)
}
for i := 0; i < b.N; i++ {
iifmUnsafe.Get(i)
}
}
func Benchmark_Unsafe_IntStrMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ismUnsafe.Get(i)
}
for i := 0; i < b.N; i++ {
ismUnsafe.Get(i)
}
}
func Benchmark_Unsafe_AnyAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ififmUnsafe.Get(i)
}
for i := 0; i < b.N; i++ {
ififmUnsafe.Get(i)
}
}
func Benchmark_Unsafe_StrIntMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
simUnsafe.Get(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
simUnsafe.Get(strconv.Itoa(i))
}
}
func Benchmark_Unsafe_StrAnyMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
sifmUnsafe.Get(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
sifmUnsafe.Get(strconv.Itoa(i))
}
}
func Benchmark_Unsafe_StrStrMap_Get(b *testing.B) {
for i := 0; i < b.N; i++ {
ssmUnsafe.Get(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
ssmUnsafe.Get(strconv.Itoa(i))
}
}

View File

@ -60,12 +60,11 @@ func Example_Normal_Basic() {
fmt.Println(m.Size())
}
func Example_Normal_Merge(){
func Example_Normal_Merge() {
m1 := gmap.New()
m2 := gmap.New()
m1.Set("key1","val1")
m2.Set("key2","val2")
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(m2)
fmt.Println(m1.Map())
}

View File

@ -74,9 +74,9 @@ func Test_IntAnyMap_Batch(t *testing.T) {
m.Removes([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: 3})
}
func Test_IntAnyMap_Iterator(t *testing.T){
func Test_IntAnyMap_Iterator(t *testing.T) {
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntAnyMapFrom(expect)
m := gmap.NewIntAnyMapFrom(expect)
m.Iterator(func(k int, v interface{}) bool {
gtest.Assert(expect[k], v)
return true
@ -95,10 +95,9 @@ func Test_IntAnyMap_Iterator(t *testing.T){
gtest.Assert(i, "2")
gtest.Assert(j, 1)
}
func Test_IntAnyMap_Lock(t *testing.T){
func Test_IntAnyMap_Lock(t *testing.T) {
expect := map[int]interface{}{1: 1, 2: "2"}
m := gmap.NewIntAnyMapFrom(expect)
m.LockFunc(func(m map[int]interface{}) {

View File

@ -75,9 +75,9 @@ func Test_IntIntMap_Batch(t *testing.T) {
gtest.Assert(m.Map(), map[int]int{3: 3})
}
func Test_IntIntMap_Iterator(t *testing.T){
func Test_IntIntMap_Iterator(t *testing.T) {
expect := map[int]int{1: 1, 2: 2}
m := gmap.NewIntIntMapFrom(expect)
m := gmap.NewIntIntMapFrom(expect)
m.Iterator(func(k int, v int) bool {
gtest.Assert(expect[k], v)
return true
@ -97,9 +97,9 @@ func Test_IntIntMap_Iterator(t *testing.T){
gtest.Assert(j, 1)
}
func Test_IntIntMap_Lock(t *testing.T){
func Test_IntIntMap_Lock(t *testing.T) {
expect := map[int]int{1: 1, 2: 2}
m := gmap.NewIntIntMapFrom(expect)
m := gmap.NewIntIntMapFrom(expect)
m.LockFunc(func(m map[int]int) {
gtest.Assert(m, expect)
})

View File

@ -74,13 +74,13 @@ func Test_IntStrMap_Batch(t *testing.T) {
m := gmap.NewIntStrMap()
m.Sets(map[int]string{1: "a", 2: "b", 3: "c"})
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b",3: "c"})
gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b", 3: "c"})
m.Removes([]int{1, 2})
gtest.Assert(m.Map(), map[int]interface{}{3: "c"})
}
func Test_IntStrMap_Iterator(t *testing.T){
func Test_IntStrMap_Iterator(t *testing.T) {
expect := map[int]string{1: "a", 2: "b"}
m := gmap.NewIntStrMapFrom(expect)
m := gmap.NewIntStrMapFrom(expect)
m.Iterator(func(k int, v string) bool {
gtest.Assert(expect[k], v)
return true
@ -100,10 +100,10 @@ func Test_IntStrMap_Iterator(t *testing.T){
gtest.Assert(j, 1)
}
func Test_IntStrMap_Lock(t *testing.T){
func Test_IntStrMap_Lock(t *testing.T) {
expect := map[int]string{1: "a", 2: "b", 3: "c"}
m := gmap.NewIntStrMapFrom(expect)
m := gmap.NewIntStrMapFrom(expect)
m.LockFunc(func(m map[int]string) {
gtest.Assert(m, expect)
})

View File

@ -65,8 +65,8 @@ func Test_List_Map_Batch(t *testing.T) {
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_List_Map_Iterator(t *testing.T){
expect :=map[interface{}]interface{}{1: 1, "key1": "val1"}
func Test_List_Map_Iterator(t *testing.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewListMapFrom(expect)
m.Iterator(func(k interface{}, v interface{}) bool {
@ -115,6 +115,6 @@ func Test_List_Map_Order(t *testing.T) {
m.Set("k1", "v1")
m.Set("k2", "v2")
m.Set("k3", "v3")
gtest.Assert(m.Keys(), g.Slice{"k1", "k2", "k3"})
gtest.Assert(m.Keys(), g.Slice{"k1", "k2", "k3"})
gtest.Assert(m.Values(), g.Slice{"v1", "v2", "v3"})
}

View File

@ -97,7 +97,7 @@ func Test_StrAnyMap_Iterator(t *testing.T) {
func Test_StrAnyMap_Lock(t *testing.T) {
expect := map[string]interface{}{"a": true, "b": false}
m := gmap.NewStrAnyMapFrom(expect)
m := gmap.NewStrAnyMapFrom(expect)
m.LockFunc(func(m map[string]interface{}) {
gtest.Assert(m, expect)
})

View File

@ -99,7 +99,7 @@ func Test_StrIntMap_Iterator(t *testing.T) {
func Test_StrIntMap_Lock(t *testing.T) {
expect := map[string]int{"a": 1, "b": 2}
m := gmap.NewStrIntMapFrom(expect)
m := gmap.NewStrIntMapFrom(expect)
m.LockFunc(func(m map[string]int) {
gtest.Assert(m, expect)
})

View File

@ -97,7 +97,7 @@ func Test_StrStrMap_Iterator(t *testing.T) {
func Test_StrStrMap_Lock(t *testing.T) {
expect := map[string]string{"a": "a", "b": "b"}
m := gmap.NewStrStrMapFrom(expect)
m := gmap.NewStrStrMapFrom(expect)
m.LockFunc(func(m map[string]string) {
gtest.Assert(m, expect)
})

View File

@ -13,7 +13,6 @@ import (
"testing"
)
func Test_Tree_Map_Basic(t *testing.T) {
gtest.Case(t, func() {
m := gmap.NewTreeMap(gutil.ComparatorString)
@ -66,7 +65,7 @@ func Test_Tree_Map_Batch(t *testing.T) {
m.Removes([]interface{}{"key1", 1})
gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"})
}
func Test_Tree_Map_Iterator(t *testing.T){
func Test_Tree_Map_Iterator(t *testing.T) {
expect := map[interface{}]interface{}{1: 1, "key1": "val1"}
m := gmap.NewTreeMapFrom(gutil.ComparatorString, expect)
@ -100,4 +99,4 @@ func Test_Tree_Map_Clone(t *testing.T) {
m_clone.Remove("key1")
//修改clone map,原 map 不影响
gtest.AssertIN("key1", m.Keys())
}
}

View File

@ -8,39 +8,38 @@
package gpool
import (
"errors"
"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"
"errors"
"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.
type Pool struct {
list *glist.List // Available/idle list.
closed *gtype.Bool // Whether the pool is closed.
Expire int64 // Max idle time(ms), after which it is recycled.
NewFunc func()(interface{}, error) // Callback function to create item.
ExpireFunc func(interface{}) // Expired destruction function for objects.
// This function needs to be defined when the pool object
// needs to perform additional destruction operations.
// Eg: net.Conn, os.File, etc.
list *glist.List // Available/idle list.
closed *gtype.Bool // Whether the pool is closed.
Expire int64 // Max idle time(ms), after which it is recycled.
NewFunc func() (interface{}, error) // Callback function to create item.
ExpireFunc func(interface{}) // Expired destruction function for objects.
// This function needs to be defined when the pool object
// needs to perform additional destruction operations.
// Eg: net.Conn, os.File, etc.
}
// Pool item.
type poolItem struct {
expire int64 // Expire time(millisecond).
value interface{} // Value.
expire int64 // Expire time(millisecond).
value interface{} // Value.
}
// Creation function for object.
type NewFunc func() (interface{}, error)
type NewFunc func() (interface{}, error)
// Destruction function for object.
type ExpireFunc func(interface{})
// New returns a new object pool.
// To ensure execution efficiency, the expiration time cannot be modified once it is set.
//
@ -49,95 +48,95 @@ type ExpireFunc func(interface{})
// expire < 0 : immediate expired after use;
// expire > 0 : timeout expired;
// Note that the expiration time unit is ** milliseconds **.
func New(expire int, newFunc NewFunc, expireFunc...ExpireFunc) *Pool {
r := &Pool {
list : glist.New(),
closed : gtype.NewBool(),
Expire : int64(expire),
NewFunc : newFunc,
}
if len(expireFunc) > 0 {
r.ExpireFunc = expireFunc[0]
}
gtimer.AddSingleton(time.Second, r.checkExpire)
return r
func New(expire int, newFunc NewFunc, expireFunc ...ExpireFunc) *Pool {
r := &Pool{
list: glist.New(),
closed: gtype.NewBool(),
Expire: int64(expire),
NewFunc: newFunc,
}
if len(expireFunc) > 0 {
r.ExpireFunc = expireFunc[0]
}
gtimer.AddSingleton(time.Second, r.checkExpire)
return r
}
// Put puts an item to pool.
func (p *Pool) Put(value interface{}) {
item := &poolItem {
value : value,
}
if p.Expire == 0 {
item.expire = 0
} else {
item.expire = gtime.Millisecond() + p.Expire
}
p.list.PushBack(item)
item := &poolItem{
value: value,
}
if p.Expire == 0 {
item.expire = 0
} else {
item.expire = gtime.Millisecond() + p.Expire
}
p.list.PushBack(item)
}
// Clear clears pool, which means it will remove all items from pool.
func (p *Pool) Clear() {
p.list.RemoveAll()
p.list.RemoveAll()
}
// Get picks an item from pool.
func (p *Pool) Get() (interface{}, error) {
for !p.closed.Val() {
if r := p.list.PopFront(); r != nil {
f := r.(*poolItem)
if f.expire == 0 || f.expire > gtime.Millisecond() {
return f.value, nil
}
} else {
break
}
}
if p.NewFunc != nil {
return p.NewFunc()
}
return nil, errors.New("pool is empty")
for !p.closed.Val() {
if r := p.list.PopFront(); r != nil {
f := r.(*poolItem)
if f.expire == 0 || f.expire > gtime.Millisecond() {
return f.value, nil
}
} else {
break
}
}
if p.NewFunc != nil {
return p.NewFunc()
}
return nil, errors.New("pool is empty")
}
// Size returns the count of available items of pool.
func (p *Pool) Size() int {
return p.list.Len()
return p.list.Len()
}
// Close closes the pool. If <p> has ExpireFunc,
// then it automatically closes all items using this function before it's closed.
func (p *Pool) Close() {
p.closed.Set(true)
p.closed.Set(true)
}
// checkExpire removes expired items from pool every second.
func (p *Pool) checkExpire() {
if p.closed.Val() {
// If p has ExpireFunc,
// then it must close all items using this function.
if p.ExpireFunc != nil {
for {
if r := p.list.PopFront(); r != nil {
p.ExpireFunc(r.(*poolItem).value)
} else {
break
}
}
}
gtimer.Exit()
}
for {
if r := p.list.PopFront(); r != nil {
item := r.(*poolItem)
if item.expire == 0 || item.expire > gtime.Millisecond() {
p.list.PushFront(item)
break
}
if p.ExpireFunc != nil {
p.ExpireFunc(item.value)
}
} else {
break
}
}
}
if p.closed.Val() {
// If p has ExpireFunc,
// then it must close all items using this function.
if p.ExpireFunc != nil {
for {
if r := p.list.PopFront(); r != nil {
p.ExpireFunc(r.(*poolItem).value)
} else {
break
}
}
}
gtimer.Exit()
}
for {
if r := p.list.PopFront(); r != nil {
item := r.(*poolItem)
if item.expire == 0 || item.expire > gtime.Millisecond() {
p.list.PushFront(item)
break
}
if p.ExpireFunc != nil {
p.ExpireFunc(item.value)
}
} else {
break
}
}
}

View File

@ -10,33 +10,33 @@ package gpool_test
import (
"github.com/gogf/gf/g/container/gpool"
"sync"
"testing"
"sync"
)
var pool = gpool.New(99999999, nil)
var pool = gpool.New(99999999, nil)
var syncp = sync.Pool{}
func BenchmarkGPoolPut(b *testing.B) {
for i := 0; i < b.N; i++ {
pool.Put(i)
}
for i := 0; i < b.N; i++ {
pool.Put(i)
}
}
func BenchmarkGPoolGet(b *testing.B) {
for i := 0; i < b.N; i++ {
pool.Get()
}
for i := 0; i < b.N; i++ {
pool.Get()
}
}
func BenchmarkSyncPoolPut(b *testing.B) {
for i := 0; i < b.N; i++ {
syncp.Put(i)
}
for i := 0; i < b.N; i++ {
syncp.Put(i)
}
}
func BenchmarkGpoolGet(b *testing.B) {
for i := 0; i < b.N; i++ {
syncp.Get()
}
}
for i := 0; i < b.N; i++ {
syncp.Get()
}
}

View File

@ -25,103 +25,103 @@ import (
)
type Queue struct {
limit int // Limit for queue size.
list *glist.List // Underlying list structure for data maintaining.
closed *gtype.Bool // Whether queue is closed.
events chan struct{} // Events for data writing.
C chan interface{} // Underlying channel for data reading.
limit int // Limit for queue size.
list *glist.List // Underlying list structure for data maintaining.
closed *gtype.Bool // Whether queue is closed.
events chan struct{} // Events for data writing.
C chan interface{} // Underlying channel for data reading.
}
const (
// Size for queue buffer.
gDEFAULT_QUEUE_SIZE = 10000
// Max batch size per-fetching from list.
gDEFAULT_MAX_BATCH_SIZE = 10
// Size for queue buffer.
gDEFAULT_QUEUE_SIZE = 10000
// Max batch size per-fetching from list.
gDEFAULT_MAX_BATCH_SIZE = 10
)
// New returns an empty queue object.
// Optional parameter <limit> is used to limit the size of the queue, which is unlimited in default.
// When <limit> is given, the queue will be static and high performance which is comparable with stdlib channel.
func New(limit...int) *Queue {
q := &Queue {
closed : gtype.NewBool(),
}
if len(limit) > 0 {
q.limit = limit[0]
q.C = make(chan interface{}, limit[0])
} else {
q.list = glist.New()
q.events = make(chan struct{}, math.MaxInt32)
q.C = make(chan interface{}, gDEFAULT_QUEUE_SIZE)
go q.startAsyncLoop()
}
return q
func New(limit ...int) *Queue {
q := &Queue{
closed: gtype.NewBool(),
}
if len(limit) > 0 {
q.limit = limit[0]
q.C = make(chan interface{}, limit[0])
} else {
q.list = glist.New()
q.events = make(chan struct{}, math.MaxInt32)
q.C = make(chan interface{}, gDEFAULT_QUEUE_SIZE)
go q.startAsyncLoop()
}
return q
}
// startAsyncLoop starts an asynchronous goroutine,
// which handles the data synchronization from list <q.list> to channel <q.C>.
func (q *Queue) startAsyncLoop() {
defer func() {
if q.closed.Val() {
_ = recover()
}
}()
for !q.closed.Val() {
<- q.events
for !q.closed.Val() {
if length := q.list.Len(); length > 0 {
if length > gDEFAULT_MAX_BATCH_SIZE {
length = gDEFAULT_MAX_BATCH_SIZE
}
for _, v := range q.list.PopFronts(length) {
// When q.C is closed, it will panic here, especially q.C is being blocked for writing.
// If any error occurs here, it will be caught by recover and be ignored.
q.C <- v
}
} else {
break
}
}
// Clear q.events to remain just one event to do the next synchronization check.
for i := 0; i < len(q.events) - 1; i++ {
<- q.events
}
}
// It should be here to close q.C.
// It's the sender's responsibility to close channel when it should be closed.
defer func() {
if q.closed.Val() {
_ = recover()
}
}()
for !q.closed.Val() {
<-q.events
for !q.closed.Val() {
if length := q.list.Len(); length > 0 {
if length > gDEFAULT_MAX_BATCH_SIZE {
length = gDEFAULT_MAX_BATCH_SIZE
}
for _, v := range q.list.PopFronts(length) {
// When q.C is closed, it will panic here, especially q.C is being blocked for writing.
// If any error occurs here, it will be caught by recover and be ignored.
q.C <- v
}
} else {
break
}
}
// Clear q.events to remain just one event to do the next synchronization check.
for i := 0; i < len(q.events)-1; i++ {
<-q.events
}
}
// It should be here to close q.C.
// It's the sender's responsibility to close channel when it should be closed.
close(q.C)
}
// Push pushes the data <v> into the queue.
// Note that it would panics if Push is called after the queue is closed.
func (q *Queue) Push(v interface{}) {
if q.limit > 0 {
q.C <- v
} else {
q.list.PushBack(v)
if len(q.events) < gDEFAULT_QUEUE_SIZE {
q.events <- struct{}{}
}
}
if q.limit > 0 {
q.C <- v
} else {
q.list.PushBack(v)
if len(q.events) < gDEFAULT_QUEUE_SIZE {
q.events <- struct{}{}
}
}
}
// Pop pops an item from the queue in FIFO way.
// Note that it would return nil immediately if Pop is called after the queue is closed.
func (q *Queue) Pop() interface{} {
return <- q.C
return <-q.C
}
// Close closes the queue.
// Notice: It would notify all goroutines return immediately,
// which are being blocked reading using Pop method.
func (q *Queue) Close() {
q.closed.Set(true)
q.closed.Set(true)
if q.events != nil {
close(q.events)
}
for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ {
q.Pop()
}
for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ {
q.Pop()
}
}
// Len returns the length of the queue.
@ -130,7 +130,7 @@ func (q *Queue) Len() (length int) {
length += q.list.Len()
}
length += len(q.C)
return
return
}
// Size is alias of Len.

View File

@ -9,42 +9,42 @@
package gqueue_test
import (
"testing"
"github.com/gogf/gf/g/container/gqueue"
"github.com/gogf/gf/g/container/gqueue"
"testing"
)
var bn = 20000000
var length = 1000000
var qstatic = gqueue.New(length)
var qdynamic = gqueue.New()
var cany = make(chan interface{}, length)
var bn = 20000000
var length = 1000000
var qstatic = gqueue.New(length)
var qdynamic = gqueue.New()
var cany = make(chan interface{}, length)
func Benchmark_Gqueue_StaticPushAndPop(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
qstatic.Push(i)
qstatic.Pop()
}
b.N = bn
for i := 0; i < b.N; i++ {
qstatic.Push(i)
qstatic.Pop()
}
}
func Benchmark_Gqueue_DynamicPush(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
qdynamic.Push(i)
}
b.N = bn
for i := 0; i < b.N; i++ {
qdynamic.Push(i)
}
}
func Benchmark_Gqueue_DynamicPop(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
qdynamic.Pop()
}
b.N = bn
for i := 0; i < b.N; i++ {
qdynamic.Pop()
}
}
func Benchmark_Channel_PushAndPop(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
cany <- i
<- cany
}
b.N = bn
for i := 0; i < b.N; i++ {
cany <- i
<-cany
}
}

View File

@ -8,118 +8,118 @@
package gring
import (
"container/ring"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/internal/rwmutex"
"container/ring"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/internal/rwmutex"
)
type Ring struct {
mu *rwmutex.RWMutex
ring *ring.Ring // Underlying ring.
len *gtype.Int // Length(already used size).
cap *gtype.Int // Capability(>=len).
dirty *gtype.Bool // Dirty, which means the len and cap should be recalculated.
// It's marked dirty when the size of ring changes.
mu *rwmutex.RWMutex
ring *ring.Ring // Underlying ring.
len *gtype.Int // Length(already used size).
cap *gtype.Int // Capability(>=len).
dirty *gtype.Bool // Dirty, which means the len and cap should be recalculated.
// It's marked dirty when the size of ring changes.
}
func New(cap int, unsafe...bool) *Ring {
return &Ring {
mu : rwmutex.New(unsafe...),
ring : ring.New(cap),
len : gtype.NewInt(),
cap : gtype.NewInt(cap),
dirty : gtype.NewBool(),
}
func New(cap int, unsafe ...bool) *Ring {
return &Ring{
mu: rwmutex.New(unsafe...),
ring: ring.New(cap),
len: gtype.NewInt(),
cap: gtype.NewInt(cap),
dirty: gtype.NewBool(),
}
}
// Val returns the item's value of current position.
func (r *Ring) Val() interface{} {
r.mu.RLock()
v := r.ring.Value
r.mu.RUnlock()
return v
r.mu.RLock()
v := r.ring.Value
r.mu.RUnlock()
return v
}
// Len returns the size of ring.
func (r *Ring) Len() int {
r.checkAndUpdateLenAndCap()
return r.len.Val()
r.checkAndUpdateLenAndCap()
return r.len.Val()
}
// Cap returns the capacity of ring.
func (r *Ring) Cap() int {
r.checkAndUpdateLenAndCap()
return r.cap.Val()
r.checkAndUpdateLenAndCap()
return r.cap.Val()
}
// Checks and updates the len and cap of ring when ring is dirty.
func (r *Ring) checkAndUpdateLenAndCap() {
if !r.dirty.Val() {
return
}
totalLen := 0
emptyLen := 0
if r.ring != nil {
r.mu.RLock()
for p := r.ring.Next(); p != r.ring; p = p.Next() {
if p.Value == nil {
emptyLen++
}
totalLen++
}
r.mu.RUnlock()
}
r.cap.Set(totalLen)
r.len.Set(totalLen - emptyLen)
r.dirty.Set(false)
func (r *Ring) checkAndUpdateLenAndCap() {
if !r.dirty.Val() {
return
}
totalLen := 0
emptyLen := 0
if r.ring != nil {
r.mu.RLock()
for p := r.ring.Next(); p != r.ring; p = p.Next() {
if p.Value == nil {
emptyLen++
}
totalLen++
}
r.mu.RUnlock()
}
r.cap.Set(totalLen)
r.len.Set(totalLen - emptyLen)
r.dirty.Set(false)
}
// Set sets value to the item of current position.
func (r *Ring) Set(value interface{}) *Ring {
r.mu.Lock()
if r.ring.Value == nil {
r.len.Add(1)
}
r.ring.Value = value
r.mu.Unlock()
return r
r.mu.Lock()
if r.ring.Value == nil {
r.len.Add(1)
}
r.ring.Value = value
r.mu.Unlock()
return r
}
// Put sets <value> to current item of ring and moves position to next item.
func (r *Ring) Put(value interface{}) *Ring {
r.mu.Lock()
if r.ring.Value == nil {
r.len.Add(1)
}
r.ring.Value = value
r.ring = r.ring.Next()
r.mu.Unlock()
return r
r.mu.Lock()
if r.ring.Value == nil {
r.len.Add(1)
}
r.ring.Value = value
r.ring = r.ring.Next()
r.mu.Unlock()
return r
}
// Move moves n % r.Len() elements backward (n < 0) or forward (n >= 0)
// in the ring and returns that ring element. r must not be empty.
func (r *Ring) Move(n int) *Ring {
r.mu.Lock()
r.ring = r.ring.Move(n)
r.mu.Unlock()
return r
r.mu.Lock()
r.ring = r.ring.Move(n)
r.mu.Unlock()
return r
}
// Prev returns the previous ring element. r must not be empty.
func (r *Ring) Prev() *Ring {
r.mu.Lock()
r.ring = r.ring.Prev()
r.mu.Unlock()
return r
r.mu.Lock()
r.ring = r.ring.Prev()
r.mu.Unlock()
return r
}
// Next returns the next ring element. r must not be empty.
func (r *Ring) Next() *Ring {
r.mu.Lock()
r.ring = r.ring.Next()
r.mu.Unlock()
return r
r.mu.Lock()
r.ring = r.ring.Next()
r.mu.Unlock()
return r
}
// Link connects ring r with ring s such that r.Next()
@ -139,14 +139,14 @@ func (r *Ring) Next() *Ring {
// last element of s after insertion.
//
func (r *Ring) Link(s *Ring) *Ring {
r.mu.Lock()
s.mu.Lock()
r.ring.Link(s.ring)
s.mu.Unlock()
r.mu.Unlock()
r.dirty.Set(true)
s.dirty.Set(true)
return r
r.mu.Lock()
s.mu.Lock()
r.ring.Link(s.ring)
s.mu.Unlock()
r.mu.Unlock()
r.dirty.Set(true)
s.dirty.Set(true)
return r
}
// Unlink removes n % r.Len() elements from the ring r, starting
@ -154,105 +154,105 @@ func (r *Ring) Link(s *Ring) *Ring {
// The result is the removed subring. r must not be empty.
//
func (r *Ring) Unlink(n int) *Ring {
r.mu.Lock()
r.ring = r.ring.Unlink(n)
r.dirty.Set(true)
r.mu.Unlock()
return r
r.mu.Lock()
r.ring = r.ring.Unlink(n)
r.dirty.Set(true)
r.mu.Unlock()
return r
}
// RLockIteratorNext iterates and locks reading forward
// with given callback function <f> within RWMutex.RLock.
// If <f> returns true, then it continues iterating; or false to stop.
func (r *Ring) RLockIteratorNext(f func(value interface{}) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
if !f(r.ring.Value) {
return
}
for p := r.ring.Next(); p != r.ring; p = p.Next() {
if !f(p.Value) {
break
}
}
r.mu.RLock()
defer r.mu.RUnlock()
if !f(r.ring.Value) {
return
}
for p := r.ring.Next(); p != r.ring; p = p.Next() {
if !f(p.Value) {
break
}
}
}
// RLockIteratorPrev iterates and locks reading backward
// with given callback function <f> within RWMutex.RLock.
// If <f> returns true, then it continues iterating; or false to stop.
func (r *Ring) RLockIteratorPrev(f func(value interface{}) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
if !f(r.ring.Value) {
return
}
for p := r.ring.Prev(); p != r.ring; p = p.Prev() {
if !f(p.Value) {
break
}
}
r.mu.RLock()
defer r.mu.RUnlock()
if !f(r.ring.Value) {
return
}
for p := r.ring.Prev(); p != r.ring; p = p.Prev() {
if !f(p.Value) {
break
}
}
}
// LockIteratorNext iterates and locks writing forward
// with given callback function <f> within RWMutex.RLock.
// If <f> returns true, then it continues iterating; or false to stop.
func (r *Ring) LockIteratorNext(f func(item *ring.Ring) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
if !f(r.ring) {
return
}
for p := r.ring.Next(); p != r.ring; p = p.Next() {
if !f(p) {
break
}
}
r.mu.RLock()
defer r.mu.RUnlock()
if !f(r.ring) {
return
}
for p := r.ring.Next(); p != r.ring; p = p.Next() {
if !f(p) {
break
}
}
}
// LockIteratorPrev iterates and locks writing backward
// with given callback function <f> within RWMutex.RLock.
// If <f> returns true, then it continues iterating; or false to stop.
func (r *Ring) LockIteratorPrev(f func(item *ring.Ring) bool) {
r.mu.RLock()
defer r.mu.RUnlock()
if !f(r.ring) {
return
}
for p := r.ring.Prev(); p != r.ring; p = p.Prev() {
if !f(p) {
break
}
}
r.mu.RLock()
defer r.mu.RUnlock()
if !f(r.ring) {
return
}
for p := r.ring.Prev(); p != r.ring; p = p.Prev() {
if !f(p) {
break
}
}
}
// SliceNext returns a copy of all item values as slice forward from current position.
func (r *Ring) SliceNext() []interface{} {
s := make([]interface{}, 0)
r.mu.RLock()
if r.ring.Value != nil {
s = append(s, r.ring.Value)
}
for p := r.ring.Next(); p != r.ring; p = p.Next() {
if p.Value != nil {
s = append(s, p.Value)
}
}
r.mu.RUnlock()
return s
s := make([]interface{}, 0)
r.mu.RLock()
if r.ring.Value != nil {
s = append(s, r.ring.Value)
}
for p := r.ring.Next(); p != r.ring; p = p.Next() {
if p.Value != nil {
s = append(s, p.Value)
}
}
r.mu.RUnlock()
return s
}
// SlicePrev returns a copy of all item values as slice backward from current position.
func (r *Ring) SlicePrev() []interface{} {
s := make([]interface{}, 0)
r.mu.RLock()
if r.ring.Value != nil {
s = append(s, r.ring.Value)
}
for p := r.ring.Prev(); p != r.ring; p = p.Prev() {
if p.Value != nil {
s = append(s, p.Value)
}
}
r.mu.RUnlock()
return s
}
s := make([]interface{}, 0)
r.mu.RLock()
if r.ring.Value != nil {
s = append(s, r.ring.Value)
}
for p := r.ring.Prev(); p != r.ring; p = p.Prev() {
if p.Value != nil {
s = append(s, p.Value)
}
}
r.mu.RUnlock()
return s
}

View File

@ -9,38 +9,38 @@
package gring
import (
"testing"
"testing"
)
var length = 10000
var r1 = New(length)
var r1 = New(length)
func BenchmarkRing_Put(b *testing.B) {
for i := 0; i < b.N; i++ {
r1.Put(i)
}
for i := 0; i < b.N; i++ {
r1.Put(i)
}
}
func BenchmarkRing_Next(b *testing.B) {
for i := 0; i < b.N; i++ {
r1.Next()
}
for i := 0; i < b.N; i++ {
r1.Next()
}
}
func BenchmarkRing_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
r1.Set(i)
}
for i := 0; i < b.N; i++ {
r1.Set(i)
}
}
func BenchmarkRing_Len(b *testing.B) {
for i := 0; i < b.N; i++ {
r1.Len()
}
for i := 0; i < b.N; i++ {
r1.Len()
}
}
func BenchmarkRing_Cap(b *testing.B) {
for i := 0; i < b.N; i++ {
r1.Cap()
}
}
for i := 0; i < b.N; i++ {
r1.Cap()
}
}

View File

@ -7,10 +7,11 @@ import (
"github.com/gogf/gf/g/test/gtest"
"testing"
)
type Student struct {
position int
name string
upgrade bool
name string
upgrade bool
}
func TestRing_Val(t *testing.T) {
@ -18,19 +19,19 @@ func TestRing_Val(t *testing.T) {
//定义cap 为3的ring类型数据
r := gring.New(3, true)
//分别给3个元素初始化赋值
r.Put(&Student{1,"jimmy", true})
r.Put(&Student{2,"tom", true})
r.Put(&Student{3,"alon", false})
r.Put(&Student{1, "jimmy", true})
r.Put(&Student{2, "tom", true})
r.Put(&Student{3, "alon", false})
//元素取值并判断和预设值是否相等
gtest.Assert(r.Val().(*Student).name,"jimmy")
gtest.Assert(r.Val().(*Student).name, "jimmy")
//从当前位置往后移两个元素
r.Move(2)
gtest.Assert(r.Val().(*Student).name,"alon")
gtest.Assert(r.Val().(*Student).name, "alon")
//更新元素值
//测试 value == nil
r.Set(nil)
gtest.Assert(r.Val(),nil)
gtest.Assert(r.Val(), nil)
//测试value != nil
r.Set(&Student{3, "jack", true})
})
@ -53,10 +54,10 @@ func TestRing_Position(t *testing.T) {
r.Put(2)
//往后移动1个元素
r.Next()
gtest.Assert(r.Val(),2)
gtest.Assert(r.Val(), 2)
//往前移动1个元素
r.Prev()
gtest.Assert(r.Val(),1)
gtest.Assert(r.Val(), 1)
})
}
@ -80,13 +81,13 @@ func TestRing_Link(t *testing.T) {
func TestRing_Unlink(t *testing.T) {
gtest.Case(t, func() {
r := gring.New(5)
for i := 0; i< 5; i++ {
r.Put(i+1)
for i := 0; i < 5; i++ {
r.Put(i + 1)
}
// 1 2 3 4
// 删除当前位置往后的2个数据返回被删除的数据
// 重新计算s len
s := r.Unlink(2) // 2 3
s := r.Unlink(2) // 2 3
gtest.Assert(s.Val(), 2)
gtest.Assert(s.Len(), 1)
})
@ -96,33 +97,33 @@ func TestRing_Slice(t *testing.T) {
gtest.Case(t, func() {
ringLen := 5
r := gring.New(ringLen)
for i := 0; i< ringLen; i++ {
r.Put(i+1)
for i := 0; i < ringLen; i++ {
r.Put(i + 1)
}
r.Move(2) // 3
array := r.SliceNext() // [3 4 5 1 2]
r.Move(2) // 3
array := r.SliceNext() // [3 4 5 1 2]
gtest.Assert(array[0], 3)
gtest.Assert(len(array), 5)
//判断array是否等于[3 4 5 1 2]
ra := []int{3,4,5,1,2}
ra := []int{3, 4, 5, 1, 2}
gtest.Assert(ra, array)
//第3个元素设为nil
r.Set(nil)
array2 := r.SliceNext() //[4 5 1 2]
array2 := r.SliceNext() //[4 5 1 2]
//返回当前位置往后不为空的元素数组长度为4
gtest.Assert(array2, g.Slice{4,5,1,2})
gtest.Assert(array2, g.Slice{4, 5, 1, 2})
array3 := r.SlicePrev() //[2 1 5 4]
gtest.Assert(array3, g.Slice{2,1,5,4})
array3 := r.SlicePrev() //[2 1 5 4]
gtest.Assert(array3, g.Slice{2, 1, 5, 4})
s := gring.New(ringLen)
for i := 0; i< ringLen; i++ {
s.Put(i+1)
for i := 0; i < ringLen; i++ {
s.Put(i + 1)
}
array4 := s.SlicePrev() // []
gtest.Assert(array4, g.Slice{1,5,4,3,2})
array4 := s.SlicePrev() // []
gtest.Assert(array4, g.Slice{1, 5, 4, 3, 2})
})
}
@ -147,15 +148,15 @@ func TestRing_RLockIterator(t *testing.T) {
return true
})
for i := 0; i< ringLen; i++ {
r.Put(i+1)
for i := 0; i < ringLen; i++ {
r.Put(i + 1)
}
//回调函数返回true,RLockIteratorNext遍历5次,期望值分别是1、2、3、4、5
i := 0
r.RLockIteratorNext(func(v interface{}) bool {
gtest.Assert(v, i+1)
i++;
i++
return true
})
@ -197,33 +198,32 @@ func TestRing_LockIterator(t *testing.T) {
})
//ring初始化元素值
for i := 0; i< ringLen; i++ {
r.Put(i+1)
for i := 0; i < ringLen; i++ {
r.Put(i + 1)
}
//往后遍历组成数据 [1,2,3,4,5]
array1 := g.Slice{1,2,3,4,5}
array1 := g.Slice{1, 2, 3, 4, 5}
ii := 0
r.LockIteratorNext(func(item *ring.Ring) bool {
//校验每一次遍历取值是否是期望值
gtest.Assert(item.Value, array1[ii])
ii++;
ii++
return true
})
//往后取3个元素组成数组
//获得 [1,5,4]
i := 0
a := g.Slice{1,5,4}
a := g.Slice{1, 5, 4}
r.LockIteratorPrev(func(item *ring.Ring) bool {
if i > 2 {
return false
}
gtest.Assert(item.Value, a[i])
i++;
i++
return true
})
})
}
}

View File

@ -8,240 +8,240 @@
package gset
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"strings"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"strings"
)
type Set struct {
mu *rwmutex.RWMutex
m map[interface{}]struct{}
mu *rwmutex.RWMutex
m map[interface{}]struct{}
}
// New create and returns a new set, which contains un-repeated items.
// The parameter <unsafe> used to specify whether using set in un-concurrent-safety,
// which is false in default.
func New(unsafe...bool) *Set {
return NewSet(unsafe...)
func New(unsafe ...bool) *Set {
return NewSet(unsafe...)
}
// See New.
func NewSet(unsafe...bool) *Set {
return &Set{
m : make(map[interface{}]struct{}),
mu : rwmutex.New(unsafe...),
}
func NewSet(unsafe ...bool) *Set {
return &Set{
m: make(map[interface{}]struct{}),
mu: rwmutex.New(unsafe...),
}
}
// NewFrom returns a new set from <items>.
// Parameter <items> can be either a variable of any type, or a slice.
func NewFrom(items interface{}, unsafe...bool) *Set {
func NewFrom(items interface{}, unsafe ...bool) *Set {
m := make(map[interface{}]struct{})
for _, v := range gconv.Interfaces(items) {
m[v] = struct{}{}
}
return &Set{
m : m,
mu : rwmutex.New(unsafe...),
m: m,
mu: rwmutex.New(unsafe...),
}
}
// Iterator iterates the set with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
func (set *Set) Iterator(f func (v interface{}) bool) *Set {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
if !f(k) {
break
}
}
return set
func (set *Set) Iterator(f func(v interface{}) bool) *Set {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
if !f(k) {
break
}
}
return set
}
// Add adds one or multiple items to the set.
func (set *Set) Add(item...interface{}) *Set {
set.mu.Lock()
for _, v := range item {
set.m[v] = struct{}{}
}
set.mu.Unlock()
return set
func (set *Set) Add(item ...interface{}) *Set {
set.mu.Lock()
for _, v := range item {
set.m[v] = struct{}{}
}
set.mu.Unlock()
return set
}
// Contains checks whether the set contains <item>.
func (set *Set) Contains(item interface{}) bool {
set.mu.RLock()
_, exists := set.m[item]
set.mu.RUnlock()
return exists
set.mu.RLock()
_, exists := set.m[item]
set.mu.RUnlock()
return exists
}
// Remove deletes <item> from set.
func (set *Set) Remove(item interface{}) *Set {
set.mu.Lock()
delete(set.m, item)
set.mu.Unlock()
return set
set.mu.Lock()
delete(set.m, item)
set.mu.Unlock()
return set
}
// Size returns the size of the set.
func (set *Set) Size() int {
set.mu.RLock()
l := len(set.m)
set.mu.RUnlock()
return l
set.mu.RLock()
l := len(set.m)
set.mu.RUnlock()
return l
}
// Clear deletes all items of the set.
func (set *Set) Clear() *Set {
set.mu.Lock()
set.m = make(map[interface{}]struct{})
set.mu.Unlock()
return set
set.mu.Lock()
set.m = make(map[interface{}]struct{})
set.mu.Unlock()
return set
}
// Slice returns the a of items of the set as slice.
func (set *Set) Slice() []interface{} {
set.mu.RLock()
i := 0
ret := make([]interface{}, len(set.m))
for item := range set.m {
ret[i] = item
i++
}
set.mu.RUnlock()
return ret
set.mu.RLock()
i := 0
ret := make([]interface{}, len(set.m))
for item := range set.m {
ret[i] = item
i++
}
set.mu.RUnlock()
return ret
}
// Join joins items with a string <glue>.
func (set *Set) Join(glue string) string {
return strings.Join(gconv.Strings(set.Slice()), ",")
return strings.Join(gconv.Strings(set.Slice()), ",")
}
// String returns items as a string, which are joined by char ','.
func (set *Set) String() string {
return set.Join(",")
return set.Join(",")
}
// LockFunc locks writing with callback function <f>.
func (set *Set) LockFunc(f func(m map[interface{}]struct{})) {
set.mu.Lock()
defer set.mu.Unlock()
f(set.m)
set.mu.Lock()
defer set.mu.Unlock()
f(set.m)
}
// RLockFunc locks reading with callback function <f>.
func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) {
set.mu.RLock()
defer set.mu.RUnlock()
f(set.m)
set.mu.RLock()
defer set.mu.RUnlock()
f(set.m)
}
// Equal checks whether the two sets equal.
func (set *Set) Equal(other *Set) bool {
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
if len(set.m) != len(other.m) {
return false
}
for key := range set.m {
if _, ok := other.m[key]; !ok {
return false
}
}
return true
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
if len(set.m) != len(other.m) {
return false
}
for key := range set.m {
if _, ok := other.m[key]; !ok {
return false
}
}
return true
}
// IsSubsetOf checks whether the current set is a sub-set of <other>.
func (set *Set) IsSubsetOf(other *Set) bool {
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
for key := range set.m {
if _, ok := other.m[key]; !ok {
return false
}
}
return true
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
for key := range set.m {
if _, ok := other.m[key]; !ok {
return false
}
}
return true
}
// Union returns a new set which is the union of <set> and <others>.
// Which means, all the items in <newSet> are in <set> or in <others>.
func (set *Set) Union(others ... *Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
newSet.m[k] = v
}
if set != other {
for k, v := range other.m {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
func (set *Set) Union(others ...*Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
newSet.m[k] = v
}
if set != other {
for k, v := range other.m {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
return
}
// Diff returns a new set which is the difference set from <set> to <others>.
// Which means, all the items in <newSet> are in <set> but not in <others>.
func (set *Set) Diff(others...*Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set == other {
continue
}
other.mu.RLock()
for k, v := range set.m {
if _, ok := other.m[k]; !ok {
newSet.m[k] = v
}
}
other.mu.RUnlock()
}
return
func (set *Set) Diff(others ...*Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set == other {
continue
}
other.mu.RLock()
for k, v := range set.m {
if _, ok := other.m[k]; !ok {
newSet.m[k] = v
}
}
other.mu.RUnlock()
}
return
}
// Intersect returns a new set which is the intersection from <set> to <others>.
// Which means, all the items in <newSet> are in <set> and also in <others>.
func (set *Set) Intersect(others...*Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
if _, ok := other.m[k]; ok {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
func (set *Set) Intersect(others ...*Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
if _, ok := other.m[k]; ok {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
}
// Complement returns a new set which is the complement from <set> to <full>.
@ -250,23 +250,23 @@ func (set *Set) Intersect(others...*Set) (newSet *Set) {
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *Set) Complement(full *Set) (newSet *Set) {
newSet = NewSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
full.mu.RLock()
defer full.mu.RUnlock()
}
for k, v := range full.m {
if _, ok := set.m[k]; !ok {
newSet.m[k] = v
}
}
return
newSet = NewSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
full.mu.RLock()
defer full.mu.RUnlock()
}
for k, v := range full.m {
if _, ok := set.m[k]; !ok {
newSet.m[k] = v
}
}
return
}
// Merge adds items from <others> sets into <set>.
func (set *Set) Merge(others ... *Set) *Set {
func (set *Set) Merge(others ...*Set) *Set {
set.mu.Lock()
defer set.mu.Unlock()
for _, other := range others {
@ -322,4 +322,4 @@ func (set *Set) Pops(size int) []interface{} {
}
}
return array
}
}

View File

@ -8,9 +8,9 @@
package gset
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"strings"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"strings"
)
type IntSet struct {
@ -21,44 +21,44 @@ type IntSet struct {
// New create and returns a new set, which contains un-repeated items.
// The parameter <unsafe> used to specify whether using set in un-concurrent-safety,
// which is false in default.
func NewIntSet(unsafe...bool) *IntSet {
func NewIntSet(unsafe ...bool) *IntSet {
return &IntSet{
m : make(map[int]struct{}),
mu : rwmutex.New(unsafe...),
m: make(map[int]struct{}),
mu: rwmutex.New(unsafe...),
}
}
// NewIntSetFrom returns a new set from <items>.
func NewIntSetFrom(items []int, unsafe...bool) *IntSet {
func NewIntSetFrom(items []int, unsafe ...bool) *IntSet {
m := make(map[int]struct{})
for _, v := range items {
m[v] = struct{}{}
}
return &IntSet{
m : m,
mu : rwmutex.New(unsafe...),
m: m,
mu: rwmutex.New(unsafe...),
}
}
// Iterator iterates the set with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
func (set *IntSet) Iterator(f func (v int) bool) *IntSet {
set.mu.RLock()
defer set.mu.RUnlock()
func (set *IntSet) Iterator(f func(v int) bool) *IntSet {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
if !f(k) {
break
}
}
return set
return set
}
// Add adds one or multiple items to the set.
func (set *IntSet) Add(item...int) *IntSet {
func (set *IntSet) Add(item ...int) *IntSet {
set.mu.Lock()
for _, v := range item {
set.m[v] = struct{}{}
}
for _, v := range item {
set.m[v] = struct{}{}
}
set.mu.Unlock()
return set
}
@ -92,7 +92,7 @@ func (set *IntSet) Clear() *IntSet {
set.mu.Lock()
set.m = make(map[int]struct{})
set.mu.Unlock()
return set
return set
}
// Slice returns the a of items of the set as slice.
@ -110,12 +110,12 @@ func (set *IntSet) Slice() []int {
// Join joins items with a string <glue>.
func (set *IntSet) Join(glue string) string {
return strings.Join(gconv.Strings(set.Slice()), ",")
return strings.Join(gconv.Strings(set.Slice()), ",")
}
// String returns items as a string, which are joined by char ','.
func (set *IntSet) String() string {
return set.Join(",")
return set.Join(",")
}
// LockFunc locks writing with callback function <f>.
@ -127,20 +127,20 @@ func (set *IntSet) LockFunc(f func(m map[int]struct{})) {
// RLockFunc locks reading with callback function <f>.
func (set *IntSet) RLockFunc(f func(m map[int]struct{})) {
set.mu.RLock()
defer set.mu.RUnlock()
f(set.m)
set.mu.RLock()
defer set.mu.RUnlock()
f(set.m)
}
// Equal checks whether the two sets equal.
func (set *IntSet) Equal(other *IntSet) bool {
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
if len(set.m) != len(other.m) {
return false
}
@ -154,88 +154,88 @@ func (set *IntSet) Equal(other *IntSet) bool {
// IsSubsetOf checks whether the current set is a sub-set of <other>.
func (set *IntSet) IsSubsetOf(other *IntSet) bool {
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
for key := range set.m {
if _, ok := other.m[key]; !ok {
return false
}
}
return true
if set == other {
return true
}
set.mu.RLock()
defer set.mu.RUnlock()
other.mu.RLock()
defer other.mu.RUnlock()
for key := range set.m {
if _, ok := other.m[key]; !ok {
return false
}
}
return true
}
// Union returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> are in <set> or in <other>.
func (set *IntSet) Union(others ... *IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
newSet.m[k] = v
}
if set != other {
for k, v := range other.m {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
func (set *IntSet) Union(others ...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
newSet.m[k] = v
}
if set != other {
for k, v := range other.m {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
return
}
// Diff returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> are in <set> but not in <other>.
func (set *IntSet) Diff(others...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set == other {
continue
}
other.mu.RLock()
for k, v := range set.m {
if _, ok := other.m[k]; !ok {
newSet.m[k] = v
}
}
other.mu.RUnlock()
}
return
func (set *IntSet) Diff(others ...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set == other {
continue
}
other.mu.RLock()
for k, v := range set.m {
if _, ok := other.m[k]; !ok {
newSet.m[k] = v
}
}
other.mu.RUnlock()
}
return
}
// Intersect returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> are in <set> and also in <other>.
func (set *IntSet) Intersect(others...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
if _, ok := other.m[k]; ok {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
func (set *IntSet) Intersect(others ...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
if _, ok := other.m[k]; ok {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
}
// Complement returns a new set which is the complement from <set> to <full>.
@ -244,23 +244,23 @@ func (set *IntSet) Intersect(others...*IntSet) (newSet *IntSet) {
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
full.mu.RLock()
defer full.mu.RUnlock()
}
for k, v := range full.m {
if _, ok := set.m[k]; !ok {
newSet.m[k] = v
}
}
return
newSet = NewIntSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
full.mu.RLock()
defer full.mu.RUnlock()
}
for k, v := range full.m {
if _, ok := set.m[k]; !ok {
newSet.m[k] = v
}
}
return
}
// Merge adds items from <others> sets into <set>.
func (set *IntSet) Merge(others ... *IntSet) *IntSet {
func (set *IntSet) Merge(others ...*IntSet) *IntSet {
set.mu.Lock()
defer set.mu.Unlock()
for _, other := range others {
@ -316,4 +316,4 @@ func (set *IntSet) Pops(size int) []int {
}
}
return array
}
}

View File

@ -8,7 +8,7 @@
package gset
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"strings"
)
@ -21,30 +21,30 @@ type StringSet struct {
// New create and returns a new set, which contains un-repeated items.
// The parameter <unsafe> used to specify whether using set in un-concurrent-safety,
// which is false in default.
func NewStringSet(unsafe...bool) *StringSet {
return &StringSet {
m : make(map[string]struct{}),
mu : rwmutex.New(unsafe...),
func NewStringSet(unsafe ...bool) *StringSet {
return &StringSet{
m: make(map[string]struct{}),
mu: rwmutex.New(unsafe...),
}
}
// NewStringSetFrom returns a new set from <items>.
func NewStringSetFrom(items []string, unsafe...bool) *StringSet {
func NewStringSetFrom(items []string, unsafe ...bool) *StringSet {
m := make(map[string]struct{})
for _, v := range items {
m[v] = struct{}{}
}
return &StringSet{
m : m,
mu : rwmutex.New(unsafe...),
m: m,
mu: rwmutex.New(unsafe...),
}
}
// Iterator iterates the set with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
func (set *StringSet) Iterator(f func (v string) bool) *StringSet {
set.mu.RLock()
defer set.mu.RUnlock()
func (set *StringSet) Iterator(f func(v string) bool) *StringSet {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.m {
if !f(k) {
break
@ -54,7 +54,7 @@ func (set *StringSet) Iterator(f func (v string) bool) *StringSet {
}
// Add adds one or multiple items to the set.
func (set *StringSet) Add(item...string) *StringSet {
func (set *StringSet) Add(item ...string) *StringSet {
set.mu.Lock()
for _, v := range item {
set.m[v] = struct{}{}
@ -76,7 +76,7 @@ func (set *StringSet) Remove(item string) *StringSet {
set.mu.Lock()
delete(set.m, item)
set.mu.Unlock()
return set
return set
}
// Size returns the size of the set.
@ -92,7 +92,7 @@ func (set *StringSet) Clear() *StringSet {
set.mu.Lock()
set.m = make(map[string]struct{})
set.mu.Unlock()
return set
return set
}
// Slice returns the a of items of the set as slice.
@ -111,12 +111,12 @@ func (set *StringSet) Slice() []string {
// Join joins items with a string <glue>.
func (set *StringSet) Join(glue string) string {
return strings.Join(set.Slice(), ",")
return strings.Join(set.Slice(), ",")
}
// String returns items as a string, which are joined by char ','.
func (set *StringSet) String() string {
return set.Join(",")
return set.Join(",")
}
// LockFunc locks writing with callback function <f>.
@ -172,71 +172,71 @@ func (set *StringSet) IsSubsetOf(other *StringSet) bool {
// Union returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> are in <set> or in <other>.
func (set *StringSet) Union(others ... *StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
newSet.m[k] = v
}
if set != other {
for k, v := range other.m {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
func (set *StringSet) Union(others ...*StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
newSet.m[k] = v
}
if set != other {
for k, v := range other.m {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
return
}
// Diff returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> are in <set> but not in <other>.
func (set *StringSet) Diff(others...*StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set == other {
continue
}
other.mu.RLock()
for k, v := range set.m {
if _, ok := other.m[k]; !ok {
newSet.m[k] = v
}
}
other.mu.RUnlock()
}
return
func (set *StringSet) Diff(others ...*StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set == other {
continue
}
other.mu.RLock()
for k, v := range set.m {
if _, ok := other.m[k]; !ok {
newSet.m[k] = v
}
}
other.mu.RUnlock()
}
return
}
// Intersect returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> are in <set> and also in <other>.
func (set *StringSet) Intersect(others...*StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
if _, ok := other.m[k]; ok {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
func (set *StringSet) Intersect(others ...*StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
if set != other {
other.mu.RLock()
}
for k, v := range set.m {
if _, ok := other.m[k]; ok {
newSet.m[k] = v
}
}
if set != other {
other.mu.RUnlock()
}
}
return
}
// Complement returns a new set which is the complement from <set> to <full>.
@ -245,23 +245,23 @@ func (set *StringSet) Intersect(others...*StringSet) (newSet *StringSet) {
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *StringSet) Complement(full *StringSet) (newSet *StringSet) {
newSet = NewStringSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
full.mu.RLock()
defer full.mu.RUnlock()
}
for k, v := range full.m {
if _, ok := set.m[k]; !ok {
newSet.m[k] = v
}
}
return
newSet = NewStringSet(true)
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
full.mu.RLock()
defer full.mu.RUnlock()
}
for k, v := range full.m {
if _, ok := set.m[k]; !ok {
newSet.m[k] = v
}
}
return
}
// Merge adds items from <others> sets into <set>.
func (set *StringSet) Merge(others ... *StringSet) *StringSet {
func (set *StringSet) Merge(others ...*StringSet) *StringSet {
set.mu.Lock()
defer set.mu.Unlock()
for _, other := range others {
@ -317,4 +317,4 @@ func (set *StringSet) Pops(size int) []string {
}
}
return array
}
}

View File

@ -9,122 +9,122 @@
package gset_test
import (
"testing"
"strconv"
"github.com/gogf/gf/g/container/gset"
"github.com/gogf/gf/g/container/gset"
"strconv"
"testing"
)
var ints = gset.NewIntSet()
var itfs = gset.NewSet()
var strs = gset.NewStringSet()
var ints = gset.NewIntSet()
var itfs = gset.NewSet()
var strs = gset.NewStringSet()
var intsUnsafe = gset.NewIntSet(true)
var itfsUnsafe = gset.NewSet(true)
var strsUnsafe = gset.NewStringSet(true)
func Benchmark_IntSet_Add(b *testing.B) {
for i := 0; i < b.N; i++ {
ints.Add(i)
}
for i := 0; i < b.N; i++ {
ints.Add(i)
}
}
func Benchmark_IntSet_Contains(b *testing.B) {
for i := 0; i < b.N; i++ {
ints.Contains(i)
}
for i := 0; i < b.N; i++ {
ints.Contains(i)
}
}
func Benchmark_IntSet_Remove(b *testing.B) {
for i := 0; i < b.N; i++ {
ints.Remove(i)
}
for i := 0; i < b.N; i++ {
ints.Remove(i)
}
}
func Benchmark_Set_Add(b *testing.B) {
for i := 0; i < b.N; i++ {
itfs.Add(i)
}
for i := 0; i < b.N; i++ {
itfs.Add(i)
}
}
func Benchmark_Set_Contains(b *testing.B) {
for i := 0; i < b.N; i++ {
itfs.Contains(i)
}
for i := 0; i < b.N; i++ {
itfs.Contains(i)
}
}
func Benchmark_Set_Remove(b *testing.B) {
for i := 0; i < b.N; i++ {
itfs.Remove(i)
}
for i := 0; i < b.N; i++ {
itfs.Remove(i)
}
}
func Benchmark_StringSet_Add(b *testing.B) {
for i := 0; i < b.N; i++ {
strs.Add(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
strs.Add(strconv.Itoa(i))
}
}
func Benchmark_StringSet_Contains(b *testing.B) {
for i := 0; i < b.N; i++ {
strs.Contains(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
strs.Contains(strconv.Itoa(i))
}
}
func Benchmark_StringSet_Remove(b *testing.B) {
for i := 0; i < b.N; i++ {
strs.Remove(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
strs.Remove(strconv.Itoa(i))
}
}
func Benchmark_Unsafe_IntSet_Add(b *testing.B) {
for i := 0; i < b.N; i++ {
intsUnsafe.Add(i)
}
for i := 0; i < b.N; i++ {
intsUnsafe.Add(i)
}
}
func Benchmark_Unsafe_IntSet_Contains(b *testing.B) {
for i := 0; i < b.N; i++ {
intsUnsafe.Contains(i)
}
for i := 0; i < b.N; i++ {
intsUnsafe.Contains(i)
}
}
func Benchmark_Unsafe_IntSet_Remove(b *testing.B) {
for i := 0; i < b.N; i++ {
intsUnsafe.Remove(i)
}
for i := 0; i < b.N; i++ {
intsUnsafe.Remove(i)
}
}
func Benchmark_Unsafe_Set_Add(b *testing.B) {
for i := 0; i < b.N; i++ {
itfsUnsafe.Add(i)
}
for i := 0; i < b.N; i++ {
itfsUnsafe.Add(i)
}
}
func Benchmark_Unsafe_Set_Contains(b *testing.B) {
for i := 0; i < b.N; i++ {
itfsUnsafe.Contains(i)
}
for i := 0; i < b.N; i++ {
itfsUnsafe.Contains(i)
}
}
func Benchmark_Unsafe_Set_Remove(b *testing.B) {
for i := 0; i < b.N; i++ {
itfsUnsafe.Remove(i)
}
for i := 0; i < b.N; i++ {
itfsUnsafe.Remove(i)
}
}
func Benchmark_Unsafe_StringSet_Add(b *testing.B) {
for i := 0; i < b.N; i++ {
strsUnsafe.Add(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
strsUnsafe.Add(strconv.Itoa(i))
}
}
func Benchmark_Unsafe_StringSet_Contains(b *testing.B) {
for i := 0; i < b.N; i++ {
strsUnsafe.Contains(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
strsUnsafe.Contains(strconv.Itoa(i))
}
}
func Benchmark_Unsafe_StringSet_Remove(b *testing.B) {
for i := 0; i < b.N; i++ {
strsUnsafe.Remove(strconv.Itoa(i))
}
}
for i := 0; i < b.N; i++ {
strsUnsafe.Remove(strconv.Itoa(i))
}
}

View File

@ -32,9 +32,9 @@ type AVLTreeNode struct {
// NewAVLTree instantiates an AVL tree with the custom key comparator.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewAVLTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *AVLTree {
func NewAVLTree(comparator func(v1, v2 interface{}) int, unsafe ...bool) *AVLTree {
return &AVLTree{
mu : rwmutex.New(unsafe...),
mu: rwmutex.New(unsafe...),
comparator: comparator,
}
}
@ -42,7 +42,7 @@ func NewAVLTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *AVLTree
// NewAVLTreeFrom instantiates an AVL tree with the custom key comparator and data map.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewAVLTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *AVLTree {
func NewAVLTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe ...bool) *AVLTree {
tree := NewAVLTree(comparator, unsafe...)
for k, v := range data {
tree.put(k, v, nil, &tree.root)
@ -88,9 +88,12 @@ func (tree *AVLTree) doSearch(key interface{}) (value interface{}, found bool) {
for n != nil {
cmp := tree.comparator(key, n.Key)
switch {
case cmp == 0: return n.Value, true
case cmp < 0: n = n.children[0]
case cmp > 0: n = n.children[1]
case cmp == 0:
return n.Value, true
case cmp < 0:
n = n.children[0]
case cmp > 0:
n = n.children[1]
}
}
return nil, false
@ -117,7 +120,7 @@ func (tree *AVLTree) doSetWithLockCheck(key interface{}, value interface{}) inte
if v, ok := tree.doSearch(key); ok {
return v
}
if f, ok := value.(func() interface {}); ok {
if f, ok := value.(func() interface{}); ok {
value = f()
}
tree.put(key, value, nil, &tree.root)
@ -254,7 +257,7 @@ func (tree *AVLTree) Size() int {
// Keys returns all keys in asc order.
func (tree *AVLTree) Keys() []interface{} {
keys := make([]interface{}, tree.Size())
keys := make([]interface{}, tree.Size())
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
keys[index] = key
@ -267,7 +270,7 @@ func (tree *AVLTree) Keys() []interface{} {
// Values returns all values in asc order based on the key.
func (tree *AVLTree) Values() []interface{} {
values := make([]interface{}, tree.Size())
index := 0
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
values[index] = value
index++
@ -283,9 +286,9 @@ func (tree *AVLTree) Left() *AVLTreeNode {
defer tree.mu.RUnlock()
node := tree.bottom(0)
if tree.mu.IsSafe() {
return &AVLTreeNode {
Key : node.Key,
Value : node.Value,
return &AVLTreeNode{
Key: node.Key,
Value: node.Value,
}
}
return node
@ -298,9 +301,9 @@ func (tree *AVLTree) Right() *AVLTreeNode {
defer tree.mu.RUnlock()
node := tree.bottom(1)
if tree.mu.IsSafe() {
return &AVLTreeNode {
Key : node.Key,
Value : node.Value,
return &AVLTreeNode{
Key: node.Key,
Value: node.Value,
}
}
return node
@ -321,11 +324,13 @@ func (tree *AVLTree) Floor(key interface{}) (floor *AVLTreeNode, found bool) {
for n != nil {
c := tree.comparator(key, n.Key)
switch {
case c == 0: return n, true
case c < 0: n = n.children[0]
case c > 0:
floor, found = n, true
n = n.children[1]
case c == 0:
return n, true
case c < 0:
n = n.children[0]
case c > 0:
floor, found = n, true
n = n.children[1]
}
}
if found {
@ -349,11 +354,13 @@ func (tree *AVLTree) Ceiling(key interface{}) (ceiling *AVLTreeNode, found bool)
for n != nil {
c := tree.comparator(key, n.Key)
switch {
case c == 0: return n, true
case c > 0: n = n.children[1]
case c < 0:
ceiling, found = n, true
n = n.children[0]
case c == 0:
return n, true
case c > 0:
n = n.children[1]
case c < 0:
ceiling, found = n, true
n = n.children[0]
}
}
if found {
@ -401,7 +408,7 @@ func (tree *AVLTree) Map() map[interface{}]interface{} {
// or else the comparator would panic.
//
// If the type of value is different with key, you pass the new <comparator>.
func (tree *AVLTree) Flip(comparator...func(v1, v2 interface{}) int) {
func (tree *AVLTree) Flip(comparator ...func(v1, v2 interface{}) int) {
t := (*AVLTree)(nil)
if len(comparator) > 0 {
t = NewAVLTree(comparator[0], !tree.mu.IsSafe())
@ -419,13 +426,13 @@ func (tree *AVLTree) Flip(comparator...func(v1, v2 interface{}) int) {
}
// Iterator is alias of IteratorAsc.
func (tree *AVLTree) Iterator(f func (key, value interface{}) bool) {
func (tree *AVLTree) Iterator(f func(key, value interface{}) bool) {
tree.IteratorAsc(f)
}
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *AVLTree) IteratorAsc(f func (key, value interface{}) bool) {
func (tree *AVLTree) IteratorAsc(f func(key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.bottom(0)
@ -439,7 +446,7 @@ func (tree *AVLTree) IteratorAsc(f func (key, value interface{}) bool) {
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *AVLTree) IteratorDesc(f func (key, value interface{}) bool) {
func (tree *AVLTree) IteratorDesc(f func(key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.bottom(1)
@ -488,7 +495,7 @@ func (tree *AVLTree) remove(key interface{}, qp **AVLTreeNode) (value interface{
if c == 0 {
tree.size--
value = q.Value
fix = true
fix = true
if q.children[1] == nil {
if q.children[0] != nil {
q.children[0].parent = q.parent
@ -568,9 +575,9 @@ func removeFix(c int8, t **AVLTreeNode) bool {
a := (c + 1) / 2
if s.children[a].b == 0 {
s = rotate(c, s)
s = rotate(c, s)
s.b = -c
*t = s
*t = s
return false
}
@ -597,15 +604,15 @@ func doubleRotate(c int8, s *AVLTreeNode) *AVLTreeNode {
p := rotate(c, s)
switch {
default:
s.b = 0
r.b = 0
case p.b == c:
s.b = -c
r.b = 0
case p.b == -c:
s.b = 0
r.b = c
default:
s.b = 0
r.b = 0
case p.b == c:
s.b = -c
r.b = 0
case p.b == -c:
s.b = 0
r.b = c
}
p.b = 0
@ -696,4 +703,4 @@ func output(node *AVLTreeNode, prefix string, isTail bool, str *string) {
}
output(node.children[0], newPrefix, true, str)
}
}
}

View File

@ -19,8 +19,8 @@ type BTree struct {
mu *rwmutex.RWMutex
root *BTreeNode
comparator func(v1, v2 interface{}) int
size int // Total number of keys in the tree
m int // order (maximum number of children)
size int // Total number of keys in the tree
m int // order (maximum number of children)
}
// BTreeNode is a single element within the tree.
@ -40,21 +40,21 @@ type BTreeEntry struct {
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
// Note that the <m> must be greater or equal than 3, or else it panics.
func NewBTree(m int, comparator func(v1, v2 interface{}) int, unsafe...bool) *BTree {
func NewBTree(m int, comparator func(v1, v2 interface{}) int, unsafe ...bool) *BTree {
if m < 3 {
panic("Invalid order, should be at least 3")
}
return &BTree{
comparator : comparator,
mu : rwmutex.New(unsafe...),
m : m,
comparator: comparator,
mu: rwmutex.New(unsafe...),
m: m,
}
}
// NewBTreeFrom instantiates a B-tree with <m> (maximum number of children), a custom key comparator and data map.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewBTreeFrom(m int, comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *BTree {
func NewBTreeFrom(m int, comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe ...bool) *BTree {
tree := NewBTree(m, comparator, unsafe...)
for k, v := range data {
tree.doSet(k, v)
@ -121,7 +121,7 @@ func (tree *BTree) doSetWithLockCheck(key interface{}, value interface{}) interf
if entry := tree.doSearch(key); entry != nil {
return entry.Value
}
if f, ok := value.(func() interface {}); ok {
if f, ok := value.(func() interface{}); ok {
value = f()
}
tree.doSet(key, value)
@ -268,7 +268,7 @@ func (tree *BTree) Size() int {
// Keys returns all keys in asc order.
func (tree *BTree) Keys() []interface{} {
keys := make([]interface{}, tree.Size())
keys := make([]interface{}, tree.Size())
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
keys[index] = key
@ -281,7 +281,7 @@ func (tree *BTree) Keys() []interface{} {
// Values returns all values in asc order based on the key.
func (tree *BTree) Values() []interface{} {
values := make([]interface{}, tree.Size())
index := 0
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
values[index] = value
index++
@ -328,7 +328,7 @@ func (tree *BTree) Right() *BTreeEntry {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.right(tree.root)
return node.Entries[len(node.Entries) - 1]
return node.Entries[len(node.Entries)-1]
}
// String returns a string representation of container (for debugging purposes)
@ -372,13 +372,13 @@ func (tree *BTree) Print() {
}
// Iterator is alias of IteratorAsc.
func (tree *BTree) Iterator(f func (key, value interface{}) bool) {
func (tree *BTree) Iterator(f func(key, value interface{}) bool) {
tree.IteratorAsc(f)
}
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *BTree) IteratorAsc(f func (key, value interface{}) bool) {
func (tree *BTree) IteratorAsc(f func(key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.left(tree.root)
@ -396,8 +396,8 @@ loop:
// Find current entry position in current node
e, _ := tree.search(node, entry.Key)
// Try to go down to the child right of the current entry
if e + 1 < len(node.Children) {
node = node.Children[e + 1]
if e+1 < len(node.Children) {
node = node.Children[e+1]
// Try to go down to the child left of the current node
for len(node.Children) > 0 {
node = node.Children[0]
@ -407,8 +407,8 @@ loop:
goto loop
}
// Above assures that we have reached a leaf node, so return the next entry in current node (if any)
if e + 1 < len(node.Entries) {
entry = node.Entries[e + 1]
if e+1 < len(node.Entries) {
entry = node.Entries[e+1]
goto loop
}
// Reached leaf node and there are no entries to the right of the current entry, so go up to the parent
@ -426,14 +426,14 @@ loop:
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *BTree) IteratorDesc(f func (key, value interface{}) bool) {
func (tree *BTree) IteratorDesc(f func(key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.right(tree.root)
if node == nil {
return
}
entry := node.Entries[len(node.Entries) - 1]
entry := node.Entries[len(node.Entries)-1]
loop:
if entry == nil {
return
@ -448,15 +448,15 @@ loop:
node = node.Children[e]
// Try to go down to the child right of the current node
for len(node.Children) > 0 {
node = node.Children[len(node.Children) - 1]
node = node.Children[len(node.Children)-1]
}
// Return the right-most entry
entry = node.Entries[len(node.Entries) - 1]
entry = node.Entries[len(node.Entries)-1]
goto loop
}
// Above assures that we have reached a leaf node, so return the previous entry in current node (if any)
if e - 1 >= 0 {
entry = node.Entries[e - 1]
if e-1 >= 0 {
entry = node.Entries[e-1]
goto loop
}
@ -466,8 +466,8 @@ loop:
// Find previous entry position in current node (note: search returns the first equal or bigger than entry)
e, _ := tree.search(node, entry.Key)
// Check that there is a previous entry position in current node
if e - 1 >= 0 {
entry = node.Entries[e - 1]
if e-1 >= 0 {
entry = node.Entries[e-1]
goto loop
}
}
@ -534,14 +534,17 @@ func (tree *BTree) middle() int {
// search searches only within the single node among its entries
func (tree *BTree) search(node *BTreeNode, key interface{}) (index int, found bool) {
low, mid, high := 0, 0, len(node.Entries) - 1
low, mid, high := 0, 0, len(node.Entries)-1
for low <= high {
mid = (high + low) / 2
compare := tree.comparator(key, node.Entries[mid].Key)
switch {
case compare > 0: low = mid + 1
case compare < 0: high = mid - 1
case compare == 0: return mid, true
case compare > 0:
low = mid + 1
case compare < 0:
high = mid - 1
case compare == 0:
return mid, true
}
}
return low, false
@ -643,8 +646,8 @@ func (tree *BTree) splitNonRoot(node *BTreeNode) {
func (tree *BTree) splitRoot() {
middle := tree.middle()
left := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[:middle]...)}
right := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[middle+1:]...)}
left := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[:middle]...)}
right := &BTreeNode{Entries: append([]*BTreeEntry(nil), tree.root.Entries[middle+1:]...)}
// Move children from the node to be split into left and right nodes
if !tree.isLeaf(tree.root) {
@ -660,9 +663,9 @@ func (tree *BTree) splitRoot() {
Children: []*BTreeNode{left, right},
}
left.Parent = newRoot
left.Parent = newRoot
right.Parent = newRoot
tree.root = newRoot
tree.root = newRoot
}
func setParent(nodes []*BTreeNode, parent *BTreeNode) {
@ -738,10 +741,10 @@ func (tree *BTree) delete(node *BTreeNode, index int) {
}
// deleting from an internal node
leftLargestNode := tree.right(node.Children[index]) // largest node in the left sub-tree (assumed to exist)
leftLargestNode := tree.right(node.Children[index]) // largest node in the left sub-tree (assumed to exist)
leftLargestEntryIndex := len(leftLargestNode.Entries) - 1
node.Entries[index] = leftLargestNode.Entries[leftLargestEntryIndex]
deletedKey := leftLargestNode.Entries[leftLargestEntryIndex].Key
node.Entries[index] = leftLargestNode.Entries[leftLargestEntryIndex]
deletedKey := leftLargestNode.Entries[leftLargestEntryIndex].Key
tree.deleteEntry(leftLargestNode, leftLargestEntryIndex)
tree.rebalance(leftLargestNode, deletedKey)
}
@ -791,16 +794,16 @@ func (tree *BTree) rebalance(node *BTreeNode, deletedKey interface{}) {
// merge with right sibling
node.Entries = append(node.Entries, node.Parent.Entries[rightSiblingIndex-1])
node.Entries = append(node.Entries, rightSibling.Entries...)
deletedKey = node.Parent.Entries[rightSiblingIndex-1].Key
deletedKey = node.Parent.Entries[rightSiblingIndex-1].Key
tree.deleteEntry(node.Parent, rightSiblingIndex-1)
tree.appendChildren(node.Parent.Children[rightSiblingIndex], node)
tree.deleteChild(node.Parent, rightSiblingIndex)
} else if leftSibling != nil {
// merge with left sibling
entries := append([]*BTreeEntry(nil), leftSibling.Entries...)
entries = append(entries, node.Parent.Entries[leftSiblingIndex])
entries := append([]*BTreeEntry(nil), leftSibling.Entries...)
entries = append(entries, node.Parent.Entries[leftSiblingIndex])
node.Entries = append(entries, node.Entries...)
deletedKey = node.Parent.Entries[leftSiblingIndex].Key
deletedKey = node.Parent.Entries[leftSiblingIndex].Key
tree.deleteEntry(node.Parent, leftSiblingIndex)
tree.prependChildren(node.Parent.Children[leftSiblingIndex], node)
tree.deleteChild(node.Parent, leftSiblingIndex)
@ -818,7 +821,7 @@ func (tree *BTree) rebalance(node *BTreeNode, deletedKey interface{}) {
}
func (tree *BTree) prependChildren(fromNode *BTreeNode, toNode *BTreeNode) {
children := append([]*BTreeNode(nil), fromNode.Children...)
children := append([]*BTreeNode(nil), fromNode.Children...)
toNode.Children = append(children, toNode.Children...)
setParent(fromNode.Children, toNode)
}
@ -841,4 +844,4 @@ func (tree *BTree) deleteChild(node *BTreeNode, index int) {
copy(node.Children[index:], node.Children[index+1:])
node.Children[len(node.Children)-1] = nil
node.Children = node.Children[:len(node.Children)-1]
}
}

View File

@ -39,9 +39,9 @@ type RedBlackTreeNode struct {
// NewRedBlackTree instantiates a red-black tree with the custom key comparator.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewRedBlackTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *RedBlackTree {
return &RedBlackTree {
mu : rwmutex.New(unsafe...),
func NewRedBlackTree(comparator func(v1, v2 interface{}) int, unsafe ...bool) *RedBlackTree {
return &RedBlackTree{
mu: rwmutex.New(unsafe...),
comparator: comparator,
}
}
@ -49,7 +49,7 @@ func NewRedBlackTree(comparator func(v1, v2 interface{}) int, unsafe...bool) *Re
// NewRedBlackTreeFrom instantiates a red-black tree with the custom key comparator and <data> map.
// The parameter <unsafe> used to specify whether using tree in un-concurrent-safety,
// which is false in default.
func NewRedBlackTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe...bool) *RedBlackTree {
func NewRedBlackTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{}]interface{}, unsafe ...bool) *RedBlackTree {
tree := NewRedBlackTree(comparator, unsafe...)
for k, v := range data {
tree.doSet(k, v)
@ -86,7 +86,7 @@ func (tree *RedBlackTree) doSet(key interface{}, value interface{}) {
if tree.root == nil {
// Assert key is of comparator's type for initial tree
tree.comparator(key, key)
tree.root = &RedBlackTreeNode{Key: key, Value: value, color: red}
tree.root = &RedBlackTreeNode{Key: key, Value: value, color: red}
insertedNode = tree.root
} else {
node := tree.root
@ -94,26 +94,26 @@ func (tree *RedBlackTree) doSet(key interface{}, value interface{}) {
for loop {
compare := tree.comparator(key, node.Key)
switch {
case compare == 0:
//node.Key = key
node.Value = value
return
case compare < 0:
if node.left == nil {
node.left = &RedBlackTreeNode{Key: key, Value: value, color: red}
insertedNode = node.left
loop = false
} else {
node = node.left
}
case compare > 0:
if node.right == nil {
node.right = &RedBlackTreeNode{Key: key, Value: value, color: red}
insertedNode = node.right
loop = false
} else {
node = node.right
}
case compare == 0:
//node.Key = key
node.Value = value
return
case compare < 0:
if node.left == nil {
node.left = &RedBlackTreeNode{Key: key, Value: value, color: red}
insertedNode = node.left
loop = false
} else {
node = node.left
}
case compare > 0:
if node.right == nil {
node.right = &RedBlackTreeNode{Key: key, Value: value, color: red}
insertedNode = node.right
loop = false
} else {
node = node.right
}
}
}
insertedNode.parent = node
@ -143,7 +143,7 @@ func (tree *RedBlackTree) doSetWithLockCheck(key interface{}, value interface{})
if node := tree.doSearch(key); node != nil {
return node.Value
}
if f, ok := value.(func() interface {}); ok {
if f, ok := value.(func() interface{}); ok {
value = f()
}
tree.doSet(key, value)
@ -251,16 +251,16 @@ func (tree *RedBlackTree) Contains(key interface{}) bool {
// doRemove removes the node from the tree by <key> without mutex.
func (tree *RedBlackTree) doRemove(key interface{}) (value interface{}) {
child := (*RedBlackTreeNode)(nil)
node := tree.doSearch(key)
node := tree.doSearch(key)
if node == nil {
return
}
value = node.Value
if node.left != nil && node.right != nil {
p := node.left.maximumNode()
node.Key = p.Key
p := node.left.maximumNode()
node.Key = p.Key
node.Value = p.Value
node = p
node = p
}
if node.left == nil || node.right == nil {
if node.right == nil {
@ -311,7 +311,7 @@ func (tree *RedBlackTree) Size() int {
// Keys returns all keys in asc order.
func (tree *RedBlackTree) Keys() []interface{} {
keys := make([]interface{}, tree.Size())
keys := make([]interface{}, tree.Size())
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
keys[index] = key
@ -324,7 +324,7 @@ func (tree *RedBlackTree) Keys() []interface{} {
// Values returns all values in asc order based on the key.
func (tree *RedBlackTree) Values() []interface{} {
values := make([]interface{}, tree.Size())
index := 0
index := 0
tree.IteratorAsc(func(key, value interface{}) bool {
values[index] = value
index++
@ -350,8 +350,8 @@ func (tree *RedBlackTree) Left() *RedBlackTreeNode {
node := tree.leftNode()
if tree.mu.IsSafe() {
return &RedBlackTreeNode{
Key : node.Key,
Value : node.Value,
Key: node.Key,
Value: node.Value,
}
}
return node
@ -364,8 +364,8 @@ func (tree *RedBlackTree) Right() *RedBlackTreeNode {
node := tree.rightNode()
if tree.mu.IsSafe() {
return &RedBlackTreeNode{
Key : node.Key,
Value : node.Value,
Key: node.Key,
Value: node.Value,
}
}
return node
@ -406,11 +406,13 @@ func (tree *RedBlackTree) Floor(key interface{}) (floor *RedBlackTreeNode, found
for n != nil {
compare := tree.comparator(key, n.Key)
switch {
case compare == 0: return n, true
case compare < 0: n = n.left
case compare > 0:
floor, found = n, true
n = n.right
case compare == 0:
return n, true
case compare < 0:
n = n.left
case compare > 0:
floor, found = n, true
n = n.right
}
}
if found {
@ -432,11 +434,13 @@ func (tree *RedBlackTree) Ceiling(key interface{}) (ceiling *RedBlackTreeNode, f
for n != nil {
compare := tree.comparator(key, n.Key)
switch {
case compare == 0: return n, true
case compare > 0: n = n.right
case compare < 0:
ceiling, found = n, true
n = n.left
case compare == 0:
return n, true
case compare > 0:
n = n.right
case compare < 0:
ceiling, found = n, true
n = n.left
}
}
if found {
@ -446,13 +450,13 @@ func (tree *RedBlackTree) Ceiling(key interface{}) (ceiling *RedBlackTreeNode, f
}
// Iterator is alias of IteratorAsc.
func (tree *RedBlackTree) Iterator(f func (key, value interface{}) bool) {
func (tree *RedBlackTree) Iterator(f func(key, value interface{}) bool) {
tree.IteratorAsc(f)
}
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *RedBlackTree) IteratorAsc(f func (key, value interface{}) bool) {
func (tree *RedBlackTree) IteratorAsc(f func(key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.leftNode()
@ -486,7 +490,7 @@ loop:
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *RedBlackTree) IteratorDesc(f func (key, value interface{}) bool) {
func (tree *RedBlackTree) IteratorDesc(f func(key, value interface{}) bool) {
tree.mu.RLock()
defer tree.mu.RUnlock()
node := tree.rightNode()
@ -559,7 +563,7 @@ func (tree *RedBlackTree) Search(key interface{}) (value interface{}, found bool
// or else the comparator would panic.
//
// If the type of value is different with key, you pass the new <comparator>.
func (tree *RedBlackTree) Flip(comparator...func(v1, v2 interface{}) int) {
func (tree *RedBlackTree) Flip(comparator ...func(v1, v2 interface{}) int) {
t := (*RedBlackTree)(nil)
if len(comparator) > 0 {
t = NewRedBlackTree(comparator[0], !tree.mu.IsSafe())
@ -611,9 +615,12 @@ func (tree *RedBlackTree) doSearch(key interface{}) *RedBlackTreeNode {
for node != nil {
compare := tree.comparator(key, node.Key)
switch {
case compare == 0: return node
case compare < 0: node = node.left
case compare > 0: node = node.right
case compare == 0:
return node
case compare < 0:
node = node.left
case compare > 0:
node = node.right
}
}
return nil
@ -650,7 +657,7 @@ func (tree *RedBlackTree) rotateLeft(node *RedBlackTreeNode) {
if right.left != nil {
right.left.parent = node
}
right.left = node
right.left = node
node.parent = right
}
@ -661,7 +668,7 @@ func (tree *RedBlackTree) rotateRight(node *RedBlackTreeNode) {
if left.right != nil {
left.right.parent = node
}
left.right = node
left.right = node
node.parent = left
}
@ -793,14 +800,14 @@ func (tree *RedBlackTree) deleteCase5(node *RedBlackTreeNode) {
tree.nodeColor(sibling) == black &&
tree.nodeColor(sibling.left) == red &&
tree.nodeColor(sibling.right) == black {
sibling.color = red
sibling.color = red
sibling.left.color = black
tree.rotateRight(sibling)
} else if node == node.parent.right &&
tree.nodeColor(sibling) == black &&
tree.nodeColor(sibling.right) == red &&
tree.nodeColor(sibling.left) == black {
sibling.color = red
sibling.color = red
sibling.right.color = black
tree.rotateLeft(sibling)
}
@ -825,4 +832,4 @@ func (tree *RedBlackTree) nodeColor(node *RedBlackTreeNode) color {
return black
}
return node.color
}
}

View File

@ -7,45 +7,45 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type Bool struct {
value int32
value int32
}
// NewBool returns a concurrent-safe object for bool type,
// with given initial value <value>.
func NewBool(value...bool) *Bool {
t := &Bool{}
if len(value) > 0 {
if value[0] {
t.value = 1
} else {
t.value = 0
}
}
return t
func NewBool(value ...bool) *Bool {
t := &Bool{}
if len(value) > 0 {
if value[0] {
t.value = 1
} else {
t.value = 0
}
}
return t
}
// Clone clones and returns a new concurrent-safe object for bool type.
func (v *Bool) Clone() *Bool {
return NewBool(v.Val())
return NewBool(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Bool) Set(value bool) (old bool) {
if value {
old = atomic.SwapInt32(&v.value, 1) == 1
} else {
old = atomic.SwapInt32(&v.value, 0) == 1
}
return
if value {
old = atomic.SwapInt32(&v.value, 1) == 1
} else {
old = atomic.SwapInt32(&v.value, 0) == 1
}
return
}
// Val atomically loads t.valueue.
func (v *Bool) Val() bool {
return atomic.LoadInt32(&v.value) > 0
return atomic.LoadInt32(&v.value) > 0
}
// Cas executes the compare-and-swap operation for value.
@ -58,4 +58,4 @@ func (v *Bool) Cas(old, new bool) bool {
newInt32 = 1
}
return atomic.CompareAndSwapInt32(&v.value, oldInt32, newInt32)
}
}

View File

@ -7,7 +7,7 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type Byte struct {
@ -16,33 +16,33 @@ type Byte struct {
// NewByte returns a concurrent-safe object for byte type,
// with given initial value <value>.
func NewByte(value...byte) *Byte {
if len(value) > 0 {
return &Byte{
value : int32(value[0]),
func NewByte(value ...byte) *Byte {
if len(value) > 0 {
return &Byte{
value: int32(value[0]),
}
}
return &Byte{}
}
return &Byte{}
}
// Clone clones and returns a new concurrent-safe object for byte type.
func (v *Byte) Clone() *Byte {
return NewByte(v.Val())
return NewByte(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Byte) Set(value byte) (old byte) {
return byte(atomic.SwapInt32(&v.value, int32(value)))
return byte(atomic.SwapInt32(&v.value, int32(value)))
}
// Val atomically loads t.value.
func (v *Byte) Val() byte {
return byte(atomic.LoadInt32(&v.value))
return byte(atomic.LoadInt32(&v.value))
}
// Add atomically adds <delta> to t.value and returns the new value.
func (v *Byte) Add(delta byte) (new byte) {
return byte(atomic.AddInt32(&v.value, int32(delta)))
return byte(atomic.AddInt32(&v.value, int32(delta)))
}
// Cas executes the compare-and-swap operation for value.

View File

@ -14,31 +14,31 @@ type Bytes struct {
// NewBytes returns a concurrent-safe object for []byte type,
// with given initial value <value>.
func NewBytes(value...[]byte) *Bytes {
t := &Bytes{}
if len(value) > 0 {
t.value.Store(value[0])
}
return t
func NewBytes(value ...[]byte) *Bytes {
t := &Bytes{}
if len(value) > 0 {
t.value.Store(value[0])
}
return t
}
// Clone clones and returns a new concurrent-safe object for []byte type.
func (v *Bytes) Clone() *Bytes {
return NewBytes(v.Val())
return NewBytes(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
// Note: The parameter <value> cannot be nil.
func (v *Bytes) Set(value []byte) (old []byte) {
old = v.Val()
v.value.Store(value)
return
old = v.Val()
v.value.Store(value)
return
}
// Val atomically loads t.value.
func (v *Bytes) Val() []byte {
if s := v.value.Load(); s != nil {
return s.([]byte)
}
return nil
if s := v.value.Load(); s != nil {
return s.([]byte)
}
return nil
}

View File

@ -18,35 +18,35 @@ type Float32 struct {
// NewFloat32 returns a concurrent-safe object for float32 type,
// with given initial value <value>.
func NewFloat32(value...float32) *Float32 {
if len(value) > 0 {
return &Float32{
value : math.Float32bits(value[0]),
func NewFloat32(value ...float32) *Float32 {
if len(value) > 0 {
return &Float32{
value: math.Float32bits(value[0]),
}
}
return &Float32{}
}
return &Float32{}
}
// Clone clones and returns a new concurrent-safe object for float32 type.
func (v *Float32) Clone() *Float32 {
return NewFloat32(v.Val())
return NewFloat32(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Float32) Set(value float32) (old float32) {
return math.Float32frombits(atomic.SwapUint32(&v.value, math.Float32bits(value)))
return math.Float32frombits(atomic.SwapUint32(&v.value, math.Float32bits(value)))
}
// Val atomically loads t.value.
func (v *Float32) Val() float32 {
return math.Float32frombits(atomic.LoadUint32(&v.value))
return math.Float32frombits(atomic.LoadUint32(&v.value))
}
// Add atomically adds <delta> to t.value and returns the new value.
func (v *Float32) Add(delta float32) (new float32) {
for {
old := math.Float32frombits(v.value)
new = old + delta
new = old + delta
if atomic.CompareAndSwapUint32(
(*uint32)(unsafe.Pointer(&v.value)),
math.Float32bits(old),

View File

@ -18,35 +18,35 @@ type Float64 struct {
// NewFloat64 returns a concurrent-safe object for float64 type,
// with given initial value <value>.
func NewFloat64(value...float64) *Float64 {
if len(value) > 0 {
return &Float64{
value : math.Float64bits(value[0]),
func NewFloat64(value ...float64) *Float64 {
if len(value) > 0 {
return &Float64{
value: math.Float64bits(value[0]),
}
}
return &Float64{}
}
return &Float64{}
}
// Clone clones and returns a new concurrent-safe object for float64 type.
func (v *Float64) Clone() *Float64 {
return NewFloat64(v.Val())
return NewFloat64(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Float64) Set(value float64) (old float64) {
return math.Float64frombits(atomic.SwapUint64(&v.value, math.Float64bits(value)))
return math.Float64frombits(atomic.SwapUint64(&v.value, math.Float64bits(value)))
}
// Val atomically loads t.value.
func (v *Float64) Val() float64 {
return math.Float64frombits(atomic.LoadUint64(&v.value))
return math.Float64frombits(atomic.LoadUint64(&v.value))
}
// Add atomically adds <delta> to t.value and returns the new value.
func (v *Float64) Add(delta float64) (new float64) {
for {
old := math.Float64frombits(v.value)
new = old + delta
new = old + delta
if atomic.CompareAndSwapUint64(
(*uint64)(unsafe.Pointer(&v.value)),
math.Float64bits(old),

View File

@ -10,6 +10,6 @@ package gtype
type Type = Interface
// See NewInterface.
func New(value ... interface{}) *Type {
return NewInterface(value...)
}
func New(value ...interface{}) *Type {
return NewInterface(value...)
}

View File

@ -9,203 +9,189 @@
package gtype
import (
"testing"
"strconv"
"github.com/gogf/gf/g/encoding/gbinary"
"sync/atomic"
"github.com/gogf/gf/g/encoding/gbinary"
"strconv"
"sync/atomic"
"testing"
)
var it = NewInt()
var it32 = NewInt32()
var it64 = NewInt64()
var uit = NewUint()
var it = NewInt()
var it32 = NewInt32()
var it64 = NewInt64()
var uit = NewUint()
var uit32 = NewUint32()
var uit64 = NewUint64()
var bl = NewBool()
var bl = NewBool()
var bytes = NewBytes()
var str = NewString()
var inf = NewInterface()
var str = NewString()
var inf = NewInterface()
var at = atomic.Value{}
var at = atomic.Value{}
func BenchmarkInt_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
it.Set(i)
}
for i := 0; i < b.N; i++ {
it.Set(i)
}
}
func BenchmarkInt_Val(b *testing.B) {
for i := 0; i < b.N; i++ {
it.Val()
}
for i := 0; i < b.N; i++ {
it.Val()
}
}
func BenchmarkInt_Add(b *testing.B) {
for i := 0; i < b.N; i++ {
it.Add(i)
}
for i := 0; i < b.N; i++ {
it.Add(i)
}
}
func BenchmarkInt32_Set(b *testing.B) {
for i := int32(0); i < int32(b.N); i++ {
it32.Set(i)
}
for i := int32(0); i < int32(b.N); i++ {
it32.Set(i)
}
}
func BenchmarkInt32_Val(b *testing.B) {
for i := int32(0); i < int32(b.N); i++ {
it32.Val()
}
for i := int32(0); i < int32(b.N); i++ {
it32.Val()
}
}
func BenchmarkInt32_Add(b *testing.B) {
for i := int32(0); i < int32(b.N); i++ {
it32.Add(i)
}
for i := int32(0); i < int32(b.N); i++ {
it32.Add(i)
}
}
func BenchmarkInt64_Set(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
it64.Set(i)
}
for i := int64(0); i < int64(b.N); i++ {
it64.Set(i)
}
}
func BenchmarkInt64_Val(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
it64.Val()
}
for i := int64(0); i < int64(b.N); i++ {
it64.Val()
}
}
func BenchmarkInt64_Add(b *testing.B) {
for i := int64(0); i < int64(b.N); i++ {
it64.Add(i)
}
for i := int64(0); i < int64(b.N); i++ {
it64.Add(i)
}
}
func BenchmarkUint_Set(b *testing.B) {
for i := uint(0); i < uint(b.N); i++ {
uit.Set(i)
}
for i := uint(0); i < uint(b.N); i++ {
uit.Set(i)
}
}
func BenchmarkUint_Val(b *testing.B) {
for i := uint(0); i < uint(b.N); i++ {
uit.Val()
}
for i := uint(0); i < uint(b.N); i++ {
uit.Val()
}
}
func BenchmarkUint_Add(b *testing.B) {
for i := uint(0); i < uint(b.N); i++ {
uit.Add(i)
}
for i := uint(0); i < uint(b.N); i++ {
uit.Add(i)
}
}
func BenchmarkUint32_Set(b *testing.B) {
for i := uint32(0); i < uint32(b.N); i++ {
uit32.Set(i)
}
for i := uint32(0); i < uint32(b.N); i++ {
uit32.Set(i)
}
}
func BenchmarkUint32_Val(b *testing.B) {
for i := uint32(0); i < uint32(b.N); i++ {
uit32.Val()
}
for i := uint32(0); i < uint32(b.N); i++ {
uit32.Val()
}
}
func BenchmarkUint32_Add(b *testing.B) {
for i := uint32(0); i < uint32(b.N); i++ {
uit32.Add(i)
}
for i := uint32(0); i < uint32(b.N); i++ {
uit32.Add(i)
}
}
func BenchmarkUint64_Set(b *testing.B) {
for i := uint64(0); i < uint64(b.N); i++ {
uit64.Set(i)
}
for i := uint64(0); i < uint64(b.N); i++ {
uit64.Set(i)
}
}
func BenchmarkUint64_Val(b *testing.B) {
for i := uint64(0); i < uint64(b.N); i++ {
uit64.Val()
}
for i := uint64(0); i < uint64(b.N); i++ {
uit64.Val()
}
}
func BenchmarkUint64_Add(b *testing.B) {
for i := uint64(0); i < uint64(b.N); i++ {
uit64.Add(i)
}
for i := uint64(0); i < uint64(b.N); i++ {
uit64.Add(i)
}
}
func BenchmarkBool_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
bl.Set(true)
}
for i := 0; i < b.N; i++ {
bl.Set(true)
}
}
func BenchmarkBool_Val(b *testing.B) {
for i := 0; i < b.N; i++ {
bl.Val()
}
for i := 0; i < b.N; i++ {
bl.Val()
}
}
func BenchmarkString_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
str.Set(strconv.Itoa(i))
}
for i := 0; i < b.N; i++ {
str.Set(strconv.Itoa(i))
}
}
func BenchmarkString_Val(b *testing.B) {
for i := 0; i < b.N; i++ {
str.Val()
}
for i := 0; i < b.N; i++ {
str.Val()
}
}
func BenchmarkBytes_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
bytes.Set(gbinary.EncodeInt(i))
}
for i := 0; i < b.N; i++ {
bytes.Set(gbinary.EncodeInt(i))
}
}
func BenchmarkBytes_Val(b *testing.B) {
for i := 0; i < b.N; i++ {
bytes.Val()
}
for i := 0; i < b.N; i++ {
bytes.Val()
}
}
func BenchmarkInterface_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
inf.Set(i)
}
for i := 0; i < b.N; i++ {
inf.Set(i)
}
}
func BenchmarkInterface_Val(b *testing.B) {
for i := 0; i < b.N; i++ {
inf.Val()
}
for i := 0; i < b.N; i++ {
inf.Val()
}
}
func BenchmarkAtomicValue_Store(b *testing.B) {
for i := 0; i < b.N; i++ {
at.Store(i)
}
for i := 0; i < b.N; i++ {
at.Store(i)
}
}
func BenchmarkAtomicValue_Load(b *testing.B) {
for i := 0; i < b.N; i++ {
at.Load()
}
for i := 0; i < b.N; i++ {
at.Load()
}
}

View File

@ -7,7 +7,7 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type Int struct {
@ -16,36 +16,36 @@ type Int struct {
// NewInt returns a concurrent-safe object for int type,
// with given initial value <value>.
func NewInt(value...int) *Int {
if len(value) > 0 {
return &Int{
value : int64(value[0]),
func NewInt(value ...int) *Int {
if len(value) > 0 {
return &Int{
value: int64(value[0]),
}
}
return &Int{}
}
return &Int{}
}
// Clone clones and returns a new concurrent-safe object for int type.
func (v *Int) Clone() *Int {
return NewInt(v.Val())
return NewInt(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Int) Set(value int) (old int) {
return int(atomic.SwapInt64(&v.value, int64(value)))
return int(atomic.SwapInt64(&v.value, int64(value)))
}
// Val atomically loads t.value.
func (v *Int) Val() int {
return int(atomic.LoadInt64(&v.value))
return int(atomic.LoadInt64(&v.value))
}
// Add atomically adds <delta> to t.value and returns the new value.
func (v *Int) Add(delta int) (new int) {
return int(atomic.AddInt64(&v.value, int64(delta)))
return int(atomic.AddInt64(&v.value, int64(delta)))
}
// Cas executes the compare-and-swap operation for value.
func (v *Int) Cas(old, new int) bool {
return atomic.CompareAndSwapInt64(&v.value, int64(old), int64(new))
}
}

View File

@ -7,7 +7,7 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type Int32 struct {
@ -16,36 +16,36 @@ type Int32 struct {
// NewInt32 returns a concurrent-safe object for int32 type,
// with given initial value <value>.
func NewInt32(value...int32) *Int32 {
if len(value) > 0 {
return &Int32{
value : value[0],
func NewInt32(value ...int32) *Int32 {
if len(value) > 0 {
return &Int32{
value: value[0],
}
}
return &Int32{}
}
return &Int32{}
}
// Clone clones and returns a new concurrent-safe object for int32 type.
func (v *Int32) Clone() *Int32 {
return NewInt32(v.Val())
return NewInt32(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Int32) Set(value int32) (old int32) {
return atomic.SwapInt32(&v.value, value)
return atomic.SwapInt32(&v.value, value)
}
// Val atomically loads t.value.
func (v *Int32) Val() int32 {
return atomic.LoadInt32(&v.value)
return atomic.LoadInt32(&v.value)
}
// Add atomically adds <delta> to t.value and returns the new value.
func (v *Int32) Add(delta int32) (new int32) {
return atomic.AddInt32(&v.value, delta)
return atomic.AddInt32(&v.value, delta)
}
// Cas executes the compare-and-swap operation for value.
func (v *Int32) Cas(old, new int32) bool {
return atomic.CompareAndSwapInt32(&v.value, old, new)
}
}

View File

@ -7,7 +7,7 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type Int64 struct {
@ -16,36 +16,36 @@ type Int64 struct {
// NewInt64 returns a concurrent-safe object for int64 type,
// with given initial value <value>.
func NewInt64(value...int64) *Int64 {
if len(value) > 0 {
return &Int64{
value : value[0],
func NewInt64(value ...int64) *Int64 {
if len(value) > 0 {
return &Int64{
value: value[0],
}
}
return &Int64{}
}
return &Int64{}
}
// Clone clones and returns a new concurrent-safe object for int64 type.
func (v *Int64) Clone() *Int64 {
return NewInt64(v.Val())
return NewInt64(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Int64) Set(value int64) (old int64) {
return atomic.SwapInt64(&v.value, value)
return atomic.SwapInt64(&v.value, value)
}
// Val atomically loads t.value.
func (v *Int64) Val() int64 {
return atomic.LoadInt64(&v.value)
return atomic.LoadInt64(&v.value)
}
// Add atomically adds <delta> to t.value and returns the new value.
func (v *Int64) Add(delta int64) int64 {
return atomic.AddInt64(&v.value, delta)
return atomic.AddInt64(&v.value, delta)
}
// Cas executes the compare-and-swap operation for value.
func (v *Int64) Cas(old, new int64) bool {
return atomic.CompareAndSwapInt64(&v.value, old, new)
}
}

View File

@ -7,7 +7,7 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type Interface struct {
@ -16,28 +16,28 @@ type Interface struct {
// NewInterface returns a concurrent-safe object for interface{} type,
// with given initial value <value>.
func NewInterface(value...interface{}) *Interface {
t := &Interface{}
if len(value) > 0 && value[0] != nil {
t.value.Store(value[0])
}
return t
func NewInterface(value ...interface{}) *Interface {
t := &Interface{}
if len(value) > 0 && value[0] != nil {
t.value.Store(value[0])
}
return t
}
// Clone clones and returns a new concurrent-safe object for interface{} type.
func (v *Interface) Clone() *Interface {
return NewInterface(v.Val())
return NewInterface(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
// Note: The parameter <value> cannot be nil.
func (v *Interface) Set(value interface{}) (old interface{}) {
old = v.Val()
v.value.Store(value)
return
old = v.Val()
v.value.Store(value)
return
}
// Val atomically loads t.value.
func (v *Interface) Val() interface{} {
return v.value.Load()
}
return v.value.Load()
}

View File

@ -7,7 +7,7 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type String struct {
@ -16,33 +16,31 @@ type String struct {
// NewString returns a concurrent-safe object for string type,
// with given initial value <value>.
func NewString(value...string) *String {
t := &String{}
if len(value) > 0 {
t.value.Store(value[0])
}
return t
func NewString(value ...string) *String {
t := &String{}
if len(value) > 0 {
t.value.Store(value[0])
}
return t
}
// Clone clones and returns a new concurrent-safe object for string type.
func (v *String) Clone() *String {
return NewString(v.Val())
return NewString(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *String) Set(value string) (old string) {
old = v.Val()
v.value.Store(value)
return
old = v.Val()
v.value.Store(value)
return
}
// Val atomically loads t.value.
func (v *String) Val() string {
s := v.value.Load()
if s != nil {
return s.(string)
}
return ""
s := v.value.Load()
if s != nil {
return s.(string)
}
return ""
}

View File

@ -7,7 +7,7 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type Uint struct {
@ -16,36 +16,36 @@ type Uint struct {
// NewUint returns a concurrent-safe object for uint type,
// with given initial value <value>.
func NewUint(value...uint) *Uint {
if len(value) > 0 {
return &Uint{
value : uint64(value[0]),
func NewUint(value ...uint) *Uint {
if len(value) > 0 {
return &Uint{
value: uint64(value[0]),
}
}
return &Uint{}
}
return &Uint{}
}
// Clone clones and returns a new concurrent-safe object for uint type.
func (v *Uint) Clone() *Uint {
return NewUint(v.Val())
return NewUint(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Uint) Set(value uint) (old uint) {
return uint(atomic.SwapUint64(&v.value, uint64(value)))
return uint(atomic.SwapUint64(&v.value, uint64(value)))
}
// Val atomically loads t.value.
func (v *Uint) Val() uint {
return uint(atomic.LoadUint64(&v.value))
return uint(atomic.LoadUint64(&v.value))
}
// Add atomically adds <delta> to t.value and returns the new value.
func (v *Uint) Add(delta uint) (new uint) {
return uint(atomic.AddUint64(&v.value, uint64(delta)))
return uint(atomic.AddUint64(&v.value, uint64(delta)))
}
// Cas executes the compare-and-swap operation for value.
func (v *Uint) Cas(old, new uint) bool {
return atomic.CompareAndSwapUint64(&v.value, uint64(old), uint64(new))
}
}

View File

@ -7,7 +7,7 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type Uint32 struct {
@ -16,36 +16,36 @@ type Uint32 struct {
// NewUint32 returns a concurrent-safe object for uint32 type,
// with given initial value <value>.
func NewUint32(value...uint32) *Uint32 {
if len(value) > 0 {
return &Uint32{
value : value[0],
func NewUint32(value ...uint32) *Uint32 {
if len(value) > 0 {
return &Uint32{
value: value[0],
}
}
return &Uint32{}
}
return &Uint32{}
}
// Clone clones and returns a new concurrent-safe object for uint32 type.
func (v *Uint32) Clone() *Uint32 {
return NewUint32(v.Val())
return NewUint32(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Uint32) Set(value uint32) (old uint32) {
return atomic.SwapUint32(&v.value, value)
return atomic.SwapUint32(&v.value, value)
}
// Val atomically loads t.value.
func (v *Uint32) Val() uint32 {
return atomic.LoadUint32(&v.value)
return atomic.LoadUint32(&v.value)
}
// Add atomically adds <delta> to t.value and returns the new value.
func (v *Uint32) Add(delta uint32) (new uint32) {
return atomic.AddUint32(&v.value, delta)
return atomic.AddUint32(&v.value, delta)
}
// Cas executes the compare-and-swap operation for value.
func (v *Uint32) Cas(old, new uint32) bool {
return atomic.CompareAndSwapUint32(&v.value, old, new)
}
}

View File

@ -7,7 +7,7 @@
package gtype
import (
"sync/atomic"
"sync/atomic"
)
type Uint64 struct {
@ -16,36 +16,36 @@ type Uint64 struct {
// NewUint64 returns a concurrent-safe object for uint64 type,
// with given initial value <value>.
func NewUint64(value...uint64) *Uint64 {
if len(value) > 0 {
return &Uint64{
value : value[0],
func NewUint64(value ...uint64) *Uint64 {
if len(value) > 0 {
return &Uint64{
value: value[0],
}
}
return &Uint64{}
}
return &Uint64{}
}
// Clone clones and returns a new concurrent-safe object for uint64 type.
func (v *Uint64) Clone() *Uint64 {
return NewUint64(v.Val())
return NewUint64(v.Val())
}
// Set atomically stores <value> into t.value and returns the previous value of t.value.
func (v *Uint64) Set(value uint64) (old uint64) {
return atomic.SwapUint64(&v.value, value)
return atomic.SwapUint64(&v.value, value)
}
// Val atomically loads t.value.
func (v *Uint64) Val() uint64 {
return atomic.LoadUint64(&v.value)
return atomic.LoadUint64(&v.value)
}
// Add atomically adds <delta> to t.value and returns the new value.
func (v *Uint64) Add(delta uint64) (new uint64) {
return atomic.AddUint64(&v.value, delta)
return atomic.AddUint64(&v.value, delta)
}
// Cas executes the compare-and-swap operation for value.
func (v *Uint64) Cas(old, new uint64) bool {
return atomic.CompareAndSwapUint64(&v.value, old, new)
}
}

View File

@ -13,133 +13,133 @@ import "testing"
var vn = New(nil)
func Benchmark_Set(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Set(i)
}
for i := 0; i < b.N; i++ {
vn.Set(i)
}
}
func Benchmark_Val(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Val()
}
for i := 0; i < b.N; i++ {
vn.Val()
}
}
func Benchmark_IsNil(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.IsNil()
}
for i := 0; i < b.N; i++ {
vn.IsNil()
}
}
func Benchmark_Bytes(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Bytes()
}
for i := 0; i < b.N; i++ {
vn.Bytes()
}
}
func Benchmark_String(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.String()
}
for i := 0; i < b.N; i++ {
vn.String()
}
}
func Benchmark_Bool(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Bool()
}
for i := 0; i < b.N; i++ {
vn.Bool()
}
}
func Benchmark_Int(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Int()
}
for i := 0; i < b.N; i++ {
vn.Int()
}
}
func Benchmark_Int8(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Int8()
}
for i := 0; i < b.N; i++ {
vn.Int8()
}
}
func Benchmark_Int16(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Int16()
}
for i := 0; i < b.N; i++ {
vn.Int16()
}
}
func Benchmark_Int32(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Int32()
}
for i := 0; i < b.N; i++ {
vn.Int32()
}
}
func Benchmark_Int64(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Int64()
}
for i := 0; i < b.N; i++ {
vn.Int64()
}
}
func Benchmark_Uint(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Uint()
}
for i := 0; i < b.N; i++ {
vn.Uint()
}
}
func Benchmark_Uint8(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Uint8()
}
for i := 0; i < b.N; i++ {
vn.Uint8()
}
}
func Benchmark_Uint16(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Uint16()
}
for i := 0; i < b.N; i++ {
vn.Uint16()
}
}
func Benchmark_Uint32(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Uint32()
}
for i := 0; i < b.N; i++ {
vn.Uint32()
}
}
func Benchmark_Uint64(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Uint64()
}
for i := 0; i < b.N; i++ {
vn.Uint64()
}
}
func Benchmark_Float32(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Float32()
}
for i := 0; i < b.N; i++ {
vn.Float32()
}
}
func Benchmark_Float64(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Float64()
}
for i := 0; i < b.N; i++ {
vn.Float64()
}
}
func Benchmark_Ints(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Ints()
}
for i := 0; i < b.N; i++ {
vn.Ints()
}
}
func Benchmark_Strings(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Strings()
}
for i := 0; i < b.N; i++ {
vn.Strings()
}
}
func Benchmark_Floats(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Floats()
}
for i := 0; i < b.N; i++ {
vn.Floats()
}
}
func Benchmark_Interfaces(b *testing.B) {
for i := 0; i < b.N; i++ {
vn.Interfaces()
}
for i := 0; i < b.N; i++ {
vn.Interfaces()
}
}

View File

@ -8,105 +8,105 @@
package gaes
import (
"bytes"
"errors"
"crypto/aes"
"crypto/cipher"
"bytes"
"crypto/aes"
"crypto/cipher"
"errors"
)
const (
ivDefValue = "I Love Go Frame!"
ivDefValue = "I Love Go Frame!"
)
// Encrypt is alias of EncryptCBC.
func Encrypt(plainText []byte, key []byte, iv...[]byte) ([]byte, error) {
return EncryptCBC(plainText, key, iv...)
func Encrypt(plainText []byte, key []byte, iv ...[]byte) ([]byte, error) {
return EncryptCBC(plainText, key, iv...)
}
// Decrypt is alias of DecryptCBC.
func Decrypt(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) {
return DecryptCBC(cipherText, key, iv...)
func Decrypt(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) {
return DecryptCBC(cipherText, key, iv...)
}
// AES加密, 使用CBC模式注意key必须为16/24/32位长度iv初始化向量为非必需参数。
func EncryptCBC(plainText []byte, key []byte, iv...[]byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
plainText = PKCS5Padding(plainText, blockSize)
ivValue := ([]byte)(nil)
if len(iv) > 0 {
ivValue = iv[0]
} else {
ivValue = []byte(ivDefValue)
}
blockMode := cipher.NewCBCEncrypter(block, ivValue)
cipherText := make([]byte, len(plainText))
blockMode.CryptBlocks(cipherText, plainText)
func EncryptCBC(plainText []byte, key []byte, iv ...[]byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
plainText = PKCS5Padding(plainText, blockSize)
ivValue := ([]byte)(nil)
if len(iv) > 0 {
ivValue = iv[0]
} else {
ivValue = []byte(ivDefValue)
}
blockMode := cipher.NewCBCEncrypter(block, ivValue)
cipherText := make([]byte, len(plainText))
blockMode.CryptBlocks(cipherText, plainText)
return cipherText, nil
return cipherText, nil
}
// AES解密, 使用CBC模式注意key必须为16/24/32位长度iv初始化向量为非必需参数
func DecryptCBC(cipherText []byte, key []byte, iv...[]byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
if len(cipherText) < blockSize {
return nil, errors.New("cipherText too short")
}
ivValue := ([]byte)(nil)
if len(iv) > 0 {
ivValue = iv[0]
} else {
ivValue = []byte(ivDefValue)
}
if len(cipherText)%blockSize != 0 {
return nil, errors.New("cipherText is not a multiple of the block size")
}
blockModel := cipher.NewCBCDecrypter(block, ivValue)
plainText := make([]byte, len(cipherText))
blockModel.CryptBlocks(plainText, cipherText)
plainText, e := PKCS5UnPadding(plainText, blockSize)
if e != nil {
return nil, e
}
return plainText, nil
func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
if len(cipherText) < blockSize {
return nil, errors.New("cipherText too short")
}
ivValue := ([]byte)(nil)
if len(iv) > 0 {
ivValue = iv[0]
} else {
ivValue = []byte(ivDefValue)
}
if len(cipherText)%blockSize != 0 {
return nil, errors.New("cipherText is not a multiple of the block size")
}
blockModel := cipher.NewCBCDecrypter(block, ivValue)
plainText := make([]byte, len(cipherText))
blockModel.CryptBlocks(plainText, cipherText)
plainText, e := PKCS5UnPadding(plainText, blockSize)
if e != nil {
return nil, e
}
return plainText, nil
}
func PKCS5Padding(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
padding := blockSize - len(src)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) {
length := len(src)
if blockSize <= 0 {
return nil, errors.New("invalid blocklen")
}
length := len(src)
if blockSize <= 0 {
return nil, errors.New("invalid blocklen")
}
if length%blockSize != 0 || length == 0 {
return nil, errors.New("invalid data len")
}
unpadding := int(src[length - 1])
unpadding := int(src[length-1])
if unpadding > blockSize || unpadding == 0 {
return nil, errors.New("invalid padding")
}
padding := src[length - unpadding:]
padding := src[length-unpadding:]
for i := 0; i < unpadding; i++ {
if padding[i] != byte(unpadding) {
return nil, errors.New("invalid padding")
}
}
return src[:(length - unpadding)], nil
return src[:(length - unpadding)], nil
}
// AES加密, 使用CFB模式。
@ -118,7 +118,7 @@ func EncryptCFB(plainText []byte, key []byte, padding *int, iv ...[]byte) ([]byt
}
blockSize := block.BlockSize()
plainText, *padding = ZeroPadding(plainText, blockSize) //补位0
ivValue := ([]byte)(nil)
ivValue := ([]byte)(nil)
if len(iv) > 0 {
ivValue = iv[0]
} else {
@ -140,7 +140,7 @@ func DecryptCFB(cipherText []byte, key []byte, unpadding int, iv ...[]byte) ([]b
if len(cipherText) < aes.BlockSize {
return nil, errors.New("cipherText too short")
}
ivValue := ([]byte)(nil)
ivValue := ([]byte)(nil)
if len(iv) > 0 {
ivValue = iv[0]
} else {
@ -162,4 +162,4 @@ func ZeroPadding(ciphertext []byte, blockSize int) ([]byte, int) {
func ZeroUnPadding(plaintext []byte, unpadding int) []byte {
length := len(plaintext)
return plaintext[:(length - unpadding)]
}
}

View File

@ -20,10 +20,10 @@ func Encrypt(v interface{}) uint32 {
// Deprecated.
func EncryptString(v string) uint32 {
return crc32.ChecksumIEEE([]byte(v))
return crc32.ChecksumIEEE([]byte(v))
}
// Deprecated.
func EncryptBytes(v []byte) uint32 {
return crc32.ChecksumIEEE(v)
return crc32.ChecksumIEEE(v)
}

View File

@ -10,14 +10,14 @@
package gdes
import (
"crypto/des"
"crypto/cipher"
"errors"
"bytes"
"crypto/cipher"
"crypto/des"
"errors"
)
const (
NOPADDING = iota
NOPADDING = iota
PKCS5PADDING
)
@ -29,7 +29,7 @@ func DesECBEncrypt(key []byte, clearText []byte, padding int) ([]byte, error) {
}
cipherText := make([]byte, len(text))
block, err := des.NewCipher(key)
if err != nil {
return nil, err
@ -37,7 +37,7 @@ func DesECBEncrypt(key []byte, clearText []byte, padding int) ([]byte, error) {
blockSize := block.BlockSize()
for i, count := 0, len(text)/blockSize; i < count; i++ {
begin, end := i * blockSize, i * blockSize + blockSize
begin, end := i*blockSize, i*blockSize+blockSize
block.Encrypt(cipherText[begin:end], text[begin:end])
}
return cipherText, nil
@ -52,8 +52,8 @@ func DesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) {
}
blockSize := block.BlockSize()
for i, count := 0, len(text)/blockSize; i < count; i++{
begin, end := i * blockSize, i * blockSize + blockSize
for i, count := 0, len(text)/blockSize; i < count; i++ {
begin, end := i*blockSize, i*blockSize+blockSize
block.Decrypt(text[begin:end], cipherText[begin:end])
}
@ -65,7 +65,7 @@ func DesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) {
}
// ECB模式3DES加密密钥长度可以是16或24位长
func TripleDesECBEncrypt(key []byte, clearText []byte, padding int) ( []byte, error) {
func TripleDesECBEncrypt(key []byte, clearText []byte, padding int) ([]byte, error) {
if len(key) != 16 && len(key) != 24 {
return nil, errors.New("key length error")
}
@ -90,15 +90,15 @@ func TripleDesECBEncrypt(key []byte, clearText []byte, padding int) ( []byte, er
blockSize := block.BlockSize()
cipherText := make([]byte, len(text))
for i, count := 0, len(text) / blockSize; i < count; i++{
begin, end := i * blockSize, i * blockSize + blockSize
for i, count := 0, len(text)/blockSize; i < count; i++ {
begin, end := i*blockSize, i*blockSize+blockSize
block.Encrypt(cipherText[begin:end], text[begin:end])
}
return cipherText, nil
}
// ECB模式3DES解密密钥长度可以是16或24位长
func TripleDesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) {
func TripleDesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, error) {
if len(key) != 16 && len(key) != 24 {
return nil, errors.New("key length error")
}
@ -118,8 +118,8 @@ func TripleDesECBDecrypt(key []byte, cipherText []byte, padding int) ([]byte, e
blockSize := block.BlockSize()
text := make([]byte, len(cipherText))
for i, count := 0, len(text) / blockSize; i < count; i++ {
begin, end := i * blockSize, i * blockSize + blockSize
for i, count := 0, len(text)/blockSize; i < count; i++ {
begin, end := i*blockSize, i*blockSize+blockSize
block.Decrypt(text[begin:end], cipherText[begin:end])
}
@ -212,7 +212,7 @@ func TripleDesCBCEncrypt(key []byte, clearText []byte, iv []byte, padding int) (
}
// CBC模式3DES解密
func TripleDesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) ( []byte, error) {
func TripleDesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int) ([]byte, error) {
if len(key) != 16 && len(key) != 24 {
return nil, errors.New("key length invalid")
}
@ -248,45 +248,45 @@ func TripleDesCBCDecrypt(key []byte, cipherText []byte, iv []byte, padding int)
// PKCS5补位
func PKCS5Padding(text []byte, blockSize int) []byte {
padding := blockSize - len(text) % blockSize
padding := blockSize - len(text)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(text, padtext...)
}
// 去除PKCS5补位
func PKCS5Unpadding(text []byte) []byte{
func PKCS5Unpadding(text []byte) []byte {
length := len(text)
padtext := int(text[length - 1])
padtext := int(text[length-1])
return text[:(length - padtext)]
}
// 补位方法
func Padding(text []byte, padding int)([]byte, error) {
func Padding(text []byte, padding int) ([]byte, error) {
switch padding {
case NOPADDING:
if len(text) % 8 != 0 {
return nil, errors.New("text length invalid")
}
case PKCS5PADDING:
return PKCS5Padding(text, 8), nil
default:
return nil, errors.New("padding type error")
case NOPADDING:
if len(text)%8 != 0 {
return nil, errors.New("text length invalid")
}
case PKCS5PADDING:
return PKCS5Padding(text, 8), nil
default:
return nil, errors.New("padding type error")
}
return text, nil
}
// 去除补位方法
func UnPadding(text []byte, padding int)([]byte, error) {
func UnPadding(text []byte, padding int) ([]byte, error) {
switch padding {
case NOPADDING:
if len(text) % 8 != 0 {
return nil, errors.New("text length invalid")
}
case PKCS5PADDING:
return PKCS5Unpadding(text), nil
default:
return nil, errors.New("padding type error.")
case NOPADDING:
if len(text)%8 != 0 {
return nil, errors.New("text length invalid")
}
case PKCS5PADDING:
return PKCS5Unpadding(text), nil
default:
return nil, errors.New("padding type error.")
}
return text, nil
}
}

View File

@ -23,7 +23,7 @@ func TestDesECB(t *testing.T) {
// encrypt test
cipherText, err := gdes.DesECBEncrypt(key, text, padding)
gtest.AssertEQ(err, nil)
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
gtest.AssertEQ(hex.EncodeToString(cipherText), result)
// decrypt test
clearText, err := gdes.DesECBDecrypt(key, cipherText, padding)
gtest.AssertEQ(err, nil)
@ -52,12 +52,12 @@ func TestDesECB(t *testing.T) {
errPadding := 5
result := "858b176da8b12503ad6a88b4fa37833d"
cipherText, err := gdes.DesECBEncrypt(key, text, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
gtest.AssertEQ(err, nil)
gtest.AssertEQ(hex.EncodeToString(cipherText), result)
// decrypt test
clearText, err := gdes.DesECBDecrypt(key, cipherText, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(string(clearText),"12345678")
gtest.AssertEQ(err, nil)
gtest.AssertEQ(string(clearText), "12345678")
// err test
errEncrypt, err := gdes.DesECBEncrypt(key, text, errPadding)
@ -77,12 +77,12 @@ func Test3DesECB(t *testing.T) {
result := "a23ee24b98c26263a23ee24b98c26263"
// encrypt test
cipherText, err := gdes.TripleDesECBEncrypt(key, text, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
gtest.AssertEQ(err, nil)
gtest.AssertEQ(hex.EncodeToString(cipherText), result)
// decrypt test
clearText, err := gdes.TripleDesECBDecrypt(key, cipherText, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(string(clearText),"1234567812345678")
gtest.AssertEQ(err, nil)
gtest.AssertEQ(string(clearText), "1234567812345678")
// err test
errEncrypt, err := gdes.DesECBEncrypt(key, text, errPadding)
gtest.AssertNE(err, nil)
@ -97,12 +97,12 @@ func Test3DesECB(t *testing.T) {
result := "37989b1effc07a6d00ff89a7d052e79f"
// encrypt test
cipherText, err := gdes.TripleDesECBEncrypt(key, text, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
gtest.AssertEQ(err, nil)
gtest.AssertEQ(hex.EncodeToString(cipherText), result)
// decrypt test
clearText, err := gdes.TripleDesECBDecrypt(key, cipherText, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(string(clearText),"123456789")
gtest.AssertEQ(err, nil)
gtest.AssertEQ(string(clearText), "123456789")
// err test, when key is err, but text and padding is right
errEncrypt, err := gdes.TripleDesECBEncrypt(errKey, text, padding)
gtest.AssertNE(err, nil)
@ -127,12 +127,12 @@ func TestDesCBC(t *testing.T) {
result := "40826a5800608c87585ca7c9efabee47"
// encrypt test
cipherText, err := gdes.DesCBCEncrypt(key, text, iv, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
gtest.AssertEQ(err, nil)
gtest.AssertEQ(hex.EncodeToString(cipherText), result)
// decrypt test
clearText, err := gdes.DesCBCDecrypt(key, cipherText, iv, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(string(clearText),"1234567812345678")
gtest.AssertEQ(err, nil)
gtest.AssertEQ(string(clearText), "1234567812345678")
// encrypt err test.
errEncrypt, err := gdes.DesCBCEncrypt(errKey, text, iv, padding)
gtest.AssertNE(err, nil)
@ -167,12 +167,12 @@ func TestDesCBC(t *testing.T) {
result := "40826a5800608c87100a25d86ac7c52c"
// encrypt test
cipherText, err := gdes.DesCBCEncrypt(key, text, iv, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
gtest.AssertEQ(err, nil)
gtest.AssertEQ(hex.EncodeToString(cipherText), result)
// decrypt test
clearText, err := gdes.DesCBCDecrypt(key, cipherText, iv, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(string(clearText),"12345678")
gtest.AssertEQ(err, nil)
gtest.AssertEQ(string(clearText), "12345678")
// err test
errEncrypt, err := gdes.DesCBCEncrypt(key, text, errIv, padding)
gtest.AssertNE(err, nil)
@ -189,12 +189,12 @@ func Test3DesCBC(t *testing.T) {
result := "bfde1394e265d5f738d5cab170c77c88"
// encrypt test
cipherText, err := gdes.TripleDesCBCEncrypt(key, text, iv, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
gtest.AssertEQ(err, nil)
gtest.AssertEQ(hex.EncodeToString(cipherText), result)
// decrypt test
clearText, err := gdes.TripleDesCBCDecrypt(key, cipherText, iv, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(string(clearText),"1234567812345678")
gtest.AssertEQ(err, nil)
gtest.AssertEQ(string(clearText), "1234567812345678")
// encrypt err test
errEncrypt, err := gdes.TripleDesCBCEncrypt(errKey, text, iv, padding)
gtest.AssertNE(err, nil)
@ -228,12 +228,12 @@ func Test3DesCBC(t *testing.T) {
result := "40826a5800608c87100a25d86ac7c52c"
// encrypt test
cipherText, err := gdes.TripleDesCBCEncrypt(key, text, iv, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(hex.EncodeToString(cipherText),result)
gtest.AssertEQ(err, nil)
gtest.AssertEQ(hex.EncodeToString(cipherText), result)
// decrypt test
clearText, err := gdes.TripleDesCBCDecrypt(key, cipherText, iv, padding)
gtest.AssertEQ(err,nil)
gtest.AssertEQ(string(clearText),"12345678")
gtest.AssertEQ(err, nil)
gtest.AssertEQ(string(clearText), "12345678")
})
}

View File

@ -8,22 +8,21 @@
package gmd5
import (
"crypto/md5"
"fmt"
"os"
"io"
"github.com/gogf/gf/g/util/gconv"
"crypto/md5"
"fmt"
"github.com/gogf/gf/g/util/gconv"
"io"
"os"
)
// Encrypt encrypts any type of variable using MD5 algorithms.
// It uses gconv package to convert <v> to its bytes type.
func Encrypt(v interface{}) string {
h := md5.New()
h.Write([]byte(gconv.Bytes(v)))
return fmt.Sprintf("%x", h.Sum(nil))
h := md5.New()
h.Write([]byte(gconv.Bytes(v)))
return fmt.Sprintf("%x", h.Sum(nil))
}
// Deprecated.
func EncryptString(v string) string {
h := md5.New()
@ -31,18 +30,17 @@ func EncryptString(v string) string {
return fmt.Sprintf("%x", h.Sum(nil))
}
// EncryptFile encrypts file content of <path> using MD5 algorithms.
func EncryptFile(path string) string {
f, e := os.Open(path)
if e != nil {
return ""
}
defer f.Close()
h := md5.New()
_, e = io.Copy(h, f)
if e != nil {
return ""
}
return fmt.Sprintf("%x", h.Sum(nil))
f, e := os.Open(path)
if e != nil {
return ""
}
defer f.Close()
h := md5.New()
_, e = io.Copy(h, f)
if e != nil {
return ""
}
return fmt.Sprintf("%x", h.Sum(nil))
}

View File

@ -35,7 +35,7 @@ func TestEncrypt(t *testing.T) {
result := "1427562bb29f88a1161590b76398ab72"
encrypt := gmd5.Encrypt(123456)
gtest.AssertEQ(encrypt,result)
gtest.AssertEQ(encrypt, result)
})
gtest.Case(t, func() {
@ -46,7 +46,7 @@ func TestEncrypt(t *testing.T) {
}
result := "70917ebce8bd2f78c736cda63870fb39"
encrypt := gmd5.Encrypt(user)
gtest.AssertEQ(encrypt,result)
gtest.AssertEQ(encrypt, result)
})
}
@ -74,5 +74,4 @@ func TestEncryptFile(t *testing.T) {
gtest.AssertEQ(errEncrypt, "")
})
}

View File

@ -8,18 +8,18 @@
package gsha1
import (
"crypto/sha1"
"encoding/hex"
"os"
"io"
"github.com/gogf/gf/g/util/gconv"
"crypto/sha1"
"encoding/hex"
"github.com/gogf/gf/g/util/gconv"
"io"
"os"
)
// Encrypt encrypts any type of variable using SHA1 algorithms.
// It uses gconv package to convert <v> to its bytes type.
func Encrypt(v interface{}) string {
r := sha1.Sum(gconv.Bytes(v))
return hex.EncodeToString(r[:])
r := sha1.Sum(gconv.Bytes(v))
return hex.EncodeToString(r[:])
}
// Deprecated.
@ -30,15 +30,15 @@ func EncryptString(s string) string {
// EncryptFile encrypts file content of <path> using SHA1 algorithms.
func EncryptFile(path string) string {
f, e := os.Open(path)
if e != nil {
return ""
}
defer f.Close()
h := sha1.New()
_, e = io.Copy(h, f)
if e != nil {
return ""
}
return hex.EncodeToString(h.Sum(nil))
}
f, e := os.Open(path)
if e != nil {
return ""
}
defer f.Close()
h := sha1.New()
_, e = io.Copy(h, f)
if e != nil {
return ""
}
return hex.EncodeToString(h.Sum(nil))
}

View File

@ -62,6 +62,6 @@ func TestEncryptFile(t *testing.T) {
gtest.AssertEQ(encryptFile, result)
// when the file is not exist,encrypt will return empty string
errEncrypt := gsha1.EncryptFile(errPath)
gtest.AssertEQ(errEncrypt,"")
gtest.AssertEQ(errEncrypt, "")
})
}

View File

@ -11,51 +11,51 @@
package gdb
import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gring"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/util/grand"
"time"
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gring"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/util/grand"
"time"
)
// 数据库操作接口
type DB interface {
// 建立数据库连接方法(开发者一般不需要直接调用)
Open(config *ConfigNode) (*sql.DB, error)
// 建立数据库连接方法(开发者一般不需要直接调用)
Open(config *ConfigNode) (*sql.DB, error)
// SQL操作方法 API
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string, execOnMaster...bool) (*sql.Stmt, error)
Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error)
// 内部实现API的方法(不同数据库可覆盖这些方法实现自定义的操作)
doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error)
doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error)
doPrepare(link dbLink, query string) (*sql.Stmt, error)
doInsert(link dbLink, table string, data interface{}, option int, batch...int) (result sql.Result, err error)
doBatchInsert(link dbLink, table string, list interface{}, option int, batch...int) (result sql.Result, err error)
doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
doDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error)
// 内部实现API的方法(不同数据库可覆盖这些方法实现自定义的操作)
doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error)
doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error)
doPrepare(link dbLink, query string) (*sql.Stmt, error)
doInsert(link dbLink, table string, data interface{}, option int, batch ...int) (result sql.Result, err error)
doBatchInsert(link dbLink, table string, list interface{}, option int, batch ...int) (result sql.Result, err error)
doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
doDelete(link dbLink, table string, condition string, args ...interface{}) (result sql.Result, err error)
// 数据库查询
GetAll(query string, args ...interface{}) (Result, error)
GetOne(query string, args ...interface{}) (Record, error)
GetValue(query string, args ...interface{}) (Value, error)
GetCount(query string, args ...interface{}) (int, error)
GetStruct(objPointer interface{}, query string, args ...interface{}) error
GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error
GetScan(objPointer interface{}, query string, args ...interface{}) error
GetCount(query string, args ...interface{}) (int, error)
GetStruct(objPointer interface{}, query string, args ...interface{}) error
GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error
GetScan(objPointer interface{}, query string, args ...interface{}) error
// 创建底层数据库master/slave链接对象
Master() (*sql.DB, error)
Slave() (*sql.DB, error)
// 创建底层数据库master/slave链接对象
Master() (*sql.DB, error)
Slave() (*sql.DB, error)
// Ping
// Ping
PingMaster() error
PingSlave() error
@ -63,14 +63,14 @@ type DB interface {
Begin() (*TX, error)
// 数据表插入/更新/保存操作
Insert(table string, data interface{}, batch...int) (sql.Result, error)
Replace(table string, data interface{}, batch...int) (sql.Result, error)
Save(table string, data interface{}, batch...int) (sql.Result, error)
Insert(table string, data interface{}, batch ...int) (sql.Result, error)
Replace(table string, data interface{}, batch ...int) (sql.Result, error)
Save(table string, data interface{}, batch ...int) (sql.Result, error)
// 数据表插入/更新/保存操作(批量)
BatchInsert(table string, list interface{}, batch...int) (sql.Result, error)
BatchReplace(table string, list interface{}, batch...int) (sql.Result, error)
BatchSave(table string, list interface{}, batch...int) (sql.Result, error)
BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error)
BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error)
BatchSave(table string, list interface{}, batch ...int) (sql.Result, error)
// 数据修改/删除
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
@ -81,31 +81,31 @@ type DB interface {
From(tables string) *Model
// 设置管理
SetDebug(debug bool)
SetSchema(schema string)
GetQueriedSqls() []*Sql
SetDebug(debug bool)
SetSchema(schema string)
GetQueriedSqls() []*Sql
GetLastSql() *Sql
PrintQueriedSqls()
SetMaxIdleConns(n int)
SetMaxOpenConns(n int)
SetConnMaxLifetime(n int)
PrintQueriedSqls()
SetMaxIdleConns(n int)
SetMaxOpenConns(n int)
SetConnMaxLifetime(n int)
// 内部方法接口
getCache() (*gcache.Cache)
getCache() *gcache.Cache
getChars() (charLeft string, charRight string)
getDebug() bool
filterFields(table string, data map[string]interface{}) map[string]interface{}
convertValue(fieldValue interface{}, fieldType string) interface{}
getTableFields(table string) (map[string]string, error)
rowsToResult(rows *sql.Rows) (Result, error)
handleSqlBeforeExec(sql string) string
filterFields(table string, data map[string]interface{}) map[string]interface{}
convertValue(fieldValue interface{}, fieldType string) interface{}
getTableFields(table string) (map[string]string, error)
rowsToResult(rows *sql.Rows) (Result, error)
handleSqlBeforeExec(sql string) string
}
// 执行底层数据库操作的核心接口
type dbLink interface {
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string) (*sql.Stmt, error)
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string) (*sql.Stmt, error)
}
// 数据库链接对象
@ -115,11 +115,11 @@ type dbBase struct {
debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性
sqls *gring.Ring // (debug=true时有效)已执行的SQL列表
cache *gcache.Cache // 数据库缓存,包括底层连接池对象缓存及查询缓存;需要注意的是,事务查询不支持查询缓存
schema *gtype.String // 手动切换的数据库名称
tables map[string]map[string]string // 数据库表结构
schema *gtype.String // 手动切换的数据库名称
tables map[string]map[string]string // 数据库表结构
maxIdleConnCount *gtype.Int // 连接池最大限制的连接数
maxOpenConnCount *gtype.Int // 连接池最大打开的连接数
maxConnLifetime *gtype.Int // (单位秒)连接对象可重复使用的时间长度
maxOpenConnCount *gtype.Int // 连接池最大打开的连接数
maxConnLifetime *gtype.Int // (单位秒)连接对象可重复使用的时间长度
}
// 执行的SQL对象
@ -142,23 +142,23 @@ type Record map[string]Value
type Result []Record
// 关联数组,绑定一条数据表记录(使用别名)
type Map = map[string]interface{}
type Map = map[string]interface{}
// 关联数组列表(索引从0开始的数组),绑定多条记录(使用别名)
type List = []Map
const (
OPTION_INSERT = 0
OPTION_REPLACE = 1
OPTION_SAVE = 2
OPTION_IGNORE = 3
gDEFAULT_BATCH_NUM = 10 // Per count for batch insert/replace/save
gDEFAULT_CONN_MAX_LIFE_TIME = 30 // Max life time for per connection in pool.
OPTION_INSERT = 0
OPTION_REPLACE = 1
OPTION_SAVE = 2
OPTION_IGNORE = 3
gDEFAULT_BATCH_NUM = 10 // Per count for batch insert/replace/save
gDEFAULT_CONN_MAX_LIFE_TIME = 30 // Max life time for per connection in pool.
)
var (
// Instance map.
instances = gmap.NewStrAnyMap()
// Instance map.
instances = gmap.NewStrAnyMap()
)
// New creates ORM DB object with global configurations.
@ -167,7 +167,7 @@ var (
func New(name ...string) (db DB, err error) {
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
group = name[0]
}
configs.RLock()
defer configs.RUnlock()
@ -176,34 +176,34 @@ func New(name ...string) (db DB, err error) {
return nil, errors.New("empty database configuration")
}
if _, ok := configs.config[group]; ok {
if node, err := getConfigNodeByGroup(group, true); err == nil {
base := &dbBase {
group : group,
debug : gtype.NewBool(),
cache : gcache.New(),
schema : gtype.NewString(),
maxIdleConnCount : gtype.NewInt(),
maxOpenConnCount : gtype.NewInt(),
maxConnLifetime : gtype.NewInt(gDEFAULT_CONN_MAX_LIFE_TIME),
}
switch node.Type {
case "mysql":
base.db = &dbMysql{dbBase : base}
case "pgsql":
base.db = &dbPgsql{dbBase : base}
case "mssql":
base.db = &dbMssql{dbBase : base}
case "sqlite":
base.db = &dbSqlite{dbBase : base}
case "oracle":
base.db = &dbOracle{dbBase : base}
default:
return nil, errors.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type))
}
return base.db, nil
} else {
return nil, err
}
if node, err := getConfigNodeByGroup(group, true); err == nil {
base := &dbBase{
group: group,
debug: gtype.NewBool(),
cache: gcache.New(),
schema: gtype.NewString(),
maxIdleConnCount: gtype.NewInt(),
maxOpenConnCount: gtype.NewInt(),
maxConnLifetime: gtype.NewInt(gDEFAULT_CONN_MAX_LIFE_TIME),
}
switch node.Type {
case "mysql":
base.db = &dbMysql{dbBase: base}
case "pgsql":
base.db = &dbPgsql{dbBase: base}
case "mssql":
base.db = &dbMssql{dbBase: base}
case "sqlite":
base.db = &dbSqlite{dbBase: base}
case "oracle":
base.db = &dbOracle{dbBase: base}
default:
return nil, errors.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type))
}
return base.db, nil
} else {
return nil, err
}
} else {
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
}
@ -213,47 +213,47 @@ func New(name ...string) (db DB, err error) {
// The parameter <name> specifies the configuration group name,
// which is DEFAULT_GROUP_NAME in default.
func Instance(name ...string) (db DB, err error) {
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
db, err = New(group)
return db
})
if v != nil {
return v.(DB), nil
}
return
group := configs.defaultGroup
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
db, err = New(group)
return db
})
if v != nil {
return v.(DB), nil
}
return
}
// 获取指定数据库角色的一个配置项,内部根据权重计算负载均衡
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
if list, ok := configs.config[group]; ok {
// 将master, slave集群列表拆分出来
masterList := make(ConfigGroup, 0)
slaveList := make(ConfigGroup, 0)
for i := 0; i < len(list); i++ {
if list[i].Role == "slave" {
slaveList = append(slaveList, list[i])
} else {
masterList = append(masterList, list[i])
}
}
if len(masterList) < 1 {
return nil, errors.New("at least one master node configuration's need to make sense")
}
if len(slaveList) < 1 {
slaveList = masterList
}
if master {
return getConfigNodeByPriority(masterList), nil
} else {
return getConfigNodeByPriority(slaveList), nil
}
} else {
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
}
if list, ok := configs.config[group]; ok {
// 将master, slave集群列表拆分出来
masterList := make(ConfigGroup, 0)
slaveList := make(ConfigGroup, 0)
for i := 0; i < len(list); i++ {
if list[i].Role == "slave" {
slaveList = append(slaveList, list[i])
} else {
masterList = append(masterList, list[i])
}
}
if len(masterList) < 1 {
return nil, errors.New("at least one master node configuration's need to make sense")
}
if len(slaveList) < 1 {
slaveList = masterList
}
if master {
return getConfigNodeByPriority(masterList), nil
} else {
return getConfigNodeByPriority(slaveList), nil
}
} else {
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
}
}
// 按照负载均衡算法(优先级配置)从数据库集群中选择一个配置节点出来使用
@ -272,11 +272,11 @@ func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode {
}
// 如果total为0表示所有连接都没有配置priority属性那么默认都是1
if total == 0 {
for i := 0; i < len(cg); i++ {
cg[i].Priority = 1
total += cg[i].Priority * 100
}
}
for i := 0; i < len(cg); i++ {
cg[i].Priority = 1
total += cg[i].Priority * 100
}
}
// 不能取到末尾的边界点
r := grand.Rand(0, total)
if r > 0 {
@ -298,53 +298,53 @@ func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode {
// 获得底层数据库链接对象
func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
// 负载均衡
node, err := getConfigNodeByGroup(bs.group, master)
if err != nil {
return nil, err
}
// 默认值设定
if node.Charset == "" {
node.Charset = "utf8"
}
v := bs.cache.GetOrSetFuncLock(node.String(), func() interface{} {
sqlDb, err = bs.db.Open(node)
if err != nil {
return nil
}
// 负载均衡
node, err := getConfigNodeByGroup(bs.group, master)
if err != nil {
return nil, err
}
// 默认值设定
if node.Charset == "" {
node.Charset = "utf8"
}
v := bs.cache.GetOrSetFuncLock(node.String(), func() interface{} {
sqlDb, err = bs.db.Open(node)
if err != nil {
return nil
}
if n := bs.maxIdleConnCount.Val(); n > 0 {
sqlDb.SetMaxIdleConns(n)
} else if node.MaxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(node.MaxIdleConnCount)
}
if n := bs.maxIdleConnCount.Val(); n > 0 {
sqlDb.SetMaxIdleConns(n)
} else if node.MaxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(node.MaxIdleConnCount)
}
if n := bs.maxOpenConnCount.Val(); n > 0 {
sqlDb.SetMaxOpenConns(n)
} else if node.MaxOpenConnCount > 0 {
sqlDb.SetMaxOpenConns(node.MaxOpenConnCount)
}
if n := bs.maxOpenConnCount.Val(); n > 0 {
sqlDb.SetMaxOpenConns(n)
} else if node.MaxOpenConnCount > 0 {
sqlDb.SetMaxOpenConns(node.MaxOpenConnCount)
}
if n := bs.maxConnLifetime.Val(); n > 0 {
sqlDb.SetConnMaxLifetime(time.Duration(n) * time.Second)
} else if node.MaxConnLifetime > 0 {
sqlDb.SetConnMaxLifetime(time.Duration(node.MaxConnLifetime) * time.Second)
}
return sqlDb
}, 0)
if v != nil && sqlDb == nil {
sqlDb = v.(*sql.DB)
}
// 是否手动选择数据库
if v := bs.schema.Val(); v != "" {
sqlDb.Exec("USE " + v)
}
return
if n := bs.maxConnLifetime.Val(); n > 0 {
sqlDb.SetConnMaxLifetime(time.Duration(n) * time.Second)
} else if node.MaxConnLifetime > 0 {
sqlDb.SetConnMaxLifetime(time.Duration(node.MaxConnLifetime) * time.Second)
}
return sqlDb
}, 0)
if v != nil && sqlDb == nil {
sqlDb = v.(*sql.DB)
}
// 是否手动选择数据库
if v := bs.schema.Val(); v != "" {
sqlDb.Exec("USE " + v)
}
return
}
// 切换当前数据库对象操作的数据库。
func (bs *dbBase) SetSchema(schema string) {
bs.schema.Set(schema)
bs.schema.Set(schema)
}
// 创建底层数据库master链接对象。
@ -354,5 +354,5 @@ func (bs *dbBase) Master() (*sql.DB, error) {
// 创建底层数据库slave链接对象。
func (bs *dbBase) Slave() (*sql.DB, error) {
return bs.getSqlDb(false)
return bs.getSqlDb(false)
}

File diff suppressed because it is too large Load Diff

View File

@ -16,10 +16,10 @@ type batchSqlResult struct {
// see sql.Result.RowsAffected
func (r *batchSqlResult) RowsAffected() (int64, error) {
return r.rowsAffected, nil
return r.rowsAffected, nil
}
// see sql.Result.LastInsertId
func (r *batchSqlResult) LastInsertId() (int64, error) {
return r.lastResult.LastInsertId()
}
return r.lastResult.LastInsertId()
}

View File

@ -8,43 +8,43 @@
package gdb
import (
"fmt"
"github.com/gogf/gf/g/container/gring"
"sync"
"fmt"
"github.com/gogf/gf/g/container/gring"
"sync"
)
const (
DEFAULT_GROUP_NAME = "default" // 默认配置名称
DEFAULT_GROUP_NAME = "default" // 默认配置名称
)
// 数据库分组配置
type Config map[string]ConfigGroup
type Config map[string]ConfigGroup
// 数据库集群配置
type ConfigGroup []ConfigNode
// 数据库单项配置
type ConfigNode struct {
Host string // 地址
Port string // 端口
User string // 账号
Pass string // 密码
Name string // 数据库名称
Type string // 数据库类型mysql, sqlite, mssql, pgsql, oracle(目前仅支持mysql)
Role string // (可选默认为master)数据库的角色用于主从操作分离至少需要有一个master参数值master, slave
Charset string // (可选,默认为 utf8)编码,默认为 utf8
Priority int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
MaxIdleConnCount int // (可选)连接池最大限制的连接数
MaxOpenConnCount int // (可选)连接池最大打开的连接数
MaxConnLifetime int // (可选,单位秒)连接对象可重复使用的时间长度
type ConfigNode struct {
Host string // 地址
Port string // 端口
User string // 账号
Pass string // 密码
Name string // 数据库名称
Type string // 数据库类型mysql, sqlite, mssql, pgsql, oracle(目前仅支持mysql)
Role string // (可选默认为master)数据库的角色用于主从操作分离至少需要有一个master参数值master, slave
Charset string // (可选,默认为 utf8)编码,默认为 utf8
Priority int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
MaxIdleConnCount int // (可选)连接池最大限制的连接数
MaxOpenConnCount int // (可选)连接池最大打开的连接数
MaxConnLifetime int // (可选,单位秒)连接对象可重复使用的时间长度
}
// 数据库配置包内对象
var configs struct {
sync.RWMutex // 并发安全互斥锁
config Config // 数据库分组配置
defaultGroup string // 默认数据库分组名称
sync.RWMutex // 并发安全互斥锁
config Config // 数据库分组配置
defaultGroup string // 默认数据库分组名称
}
// 数据库集群配置示例,支持主从处理,多数据库集群支持
@ -80,103 +80,103 @@ var DatabaseConfiguration = Config {
// 包初始化
func init() {
configs.config = make(Config)
configs.defaultGroup = DEFAULT_GROUP_NAME
configs.config = make(Config)
configs.defaultGroup = DEFAULT_GROUP_NAME
}
// 设置当前应用的数据库配置信息,进行全局数据库配置覆盖操作
func SetConfig (config Config) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config = config
func SetConfig(config Config) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config = config
}
// 添加数据库服务器集群配置
func AddConfigGroup (group string, nodes ConfigGroup) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = nodes
func AddConfigGroup(group string, nodes ConfigGroup) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = nodes
}
// 添加一台数据库服务器配置
func AddConfigNode (group string, node ConfigNode) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = append(configs.config[group], node)
func AddConfigNode(group string, node ConfigNode) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.config[group] = append(configs.config[group], node)
}
// 添加默认链接的一台数据库服务器配置
func AddDefaultConfigNode (node ConfigNode) {
AddConfigNode(DEFAULT_GROUP_NAME, node)
func AddDefaultConfigNode(node ConfigNode) {
AddConfigNode(DEFAULT_GROUP_NAME, node)
}
// 添加默认链接的数据库服务器集群配置
func AddDefaultConfigGroup (nodes ConfigGroup) {
AddConfigGroup(DEFAULT_GROUP_NAME, nodes)
func AddDefaultConfigGroup(nodes ConfigGroup) {
AddConfigGroup(DEFAULT_GROUP_NAME, nodes)
}
// 添加一台数据库服务器配置
func GetConfig (group string) ConfigGroup {
configs.RLock()
defer configs.RUnlock()
return configs.config[group]
func GetConfig(group string) ConfigGroup {
configs.RLock()
defer configs.RUnlock()
return configs.config[group]
}
// 设置默认链接的数据库链接配置项(默认是 default)
func SetDefaultGroup (name string) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.defaultGroup = name
func SetDefaultGroup(name string) {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
configs.defaultGroup = name
}
// 获取默认链接的数据库链接配置项(默认是 default)
func GetDefaultGroup() string {
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
return configs.defaultGroup
defer instances.Clear()
configs.Lock()
defer configs.Unlock()
return configs.defaultGroup
}
// 设置数据库连接池中空闲链接的大小
func (bs *dbBase) SetMaxIdleConns(n int) {
bs.maxIdleConnCount.Set(n)
bs.maxIdleConnCount.Set(n)
}
// 设置数据库连接池最大打开的链接数量
func (bs *dbBase) SetMaxOpenConns(n int) {
bs.maxOpenConnCount.Set(n)
bs.maxOpenConnCount.Set(n)
}
// 设置数据库连接可重复利用的时间,超过该时间则被关闭废弃
// 如果 d <= 0 表示该链接会一直重复利用
func (bs *dbBase) SetConnMaxLifetime(n int) {
bs.maxConnLifetime.Set(n)
bs.maxConnLifetime.Set(n)
}
// 节点配置转换为字符串
func (node *ConfigNode) String() string {
if node.LinkInfo != "" {
return node.LinkInfo
}
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%d-%d-%d`, node.User, node.Host, node.Port,
node.Name, node.Type, node.Role, node.Charset,
node.MaxIdleConnCount, node.MaxOpenConnCount, node.MaxConnLifetime,
)
if node.LinkInfo != "" {
return node.LinkInfo
}
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%d-%d-%d`, node.User, node.Host, node.Port,
node.Name, node.Type, node.Role, node.Charset,
node.MaxIdleConnCount, node.MaxOpenConnCount, node.MaxConnLifetime,
)
}
// 是否开启调试服务
func (bs *dbBase) SetDebug(debug bool) {
bs.debug.Set(debug)
if debug && bs.sqls == nil {
bs.sqls = gring.New(gDEFAULT_DEBUG_SQL_LENGTH)
}
bs.debug.Set(debug)
if debug && bs.sqls == nil {
bs.sqls = gring.New(gDEFAULT_DEBUG_SQL_LENGTH)
}
}
// 获取是否开启调试服务
func (bs *dbBase) getDebug() bool {
return bs.debug.Val()
}
return bs.debug.Val()
}

View File

@ -27,179 +27,182 @@ type apiString interface {
// 格式化SQL查询条件
func formatCondition(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) {
// 条件字符串处理
buffer := bytes.NewBuffer(nil)
// 使用反射进行类型判断
rv := reflect.ValueOf(where)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
tmpArgs := []interface{}(nil)
switch kind {
// map/struct类型
case reflect.Map: fallthrough
case reflect.Struct:
for key, value := range structToMap(where) {
if buffer.Len() > 0 {
buffer.WriteString(" AND ")
}
// 支持slice键值/属性,如果只有一个?占位符号那么作为IN查询否则打散作为多个查询参数
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice: fallthrough
case reflect.Array:
count := gstr.Count(key, "?")
if count == 0 {
buffer.WriteString(key + " IN(?)")
tmpArgs = append(tmpArgs, value)
} else if count != rv.Len() {
buffer.WriteString(key)
tmpArgs = append(tmpArgs, value)
} else {
buffer.WriteString(key)
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
tmpArgs = append(tmpArgs, gconv.Interfaces(value)...)
}
default:
if value == nil {
buffer.WriteString(key)
} else {
if gstr.Pos(key, "?") == -1 {
if gstr.Pos(key, "<") == -1 && gstr.Pos(key, ">") == -1 && gstr.Pos(key, "=") == -1 {
buffer.WriteString(key + "=?")
} else {
buffer.WriteString(key + "?")
}
} else {
buffer.WriteString(key)
}
tmpArgs = append(tmpArgs, value)
}
}
}
// 条件字符串处理
buffer := bytes.NewBuffer(nil)
// 使用反射进行类型判断
rv := reflect.ValueOf(where)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
tmpArgs := []interface{}(nil)
switch kind {
// map/struct类型
case reflect.Map:
fallthrough
case reflect.Struct:
for key, value := range structToMap(where) {
if buffer.Len() > 0 {
buffer.WriteString(" AND ")
}
// 支持slice键值/属性,如果只有一个?占位符号那么作为IN查询否则打散作为多个查询参数
rv := reflect.ValueOf(value)
switch rv.Kind() {
case reflect.Slice:
fallthrough
case reflect.Array:
count := gstr.Count(key, "?")
if count == 0 {
buffer.WriteString(key + " IN(?)")
tmpArgs = append(tmpArgs, value)
} else if count != rv.Len() {
buffer.WriteString(key)
tmpArgs = append(tmpArgs, value)
} else {
buffer.WriteString(key)
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
tmpArgs = append(tmpArgs, gconv.Interfaces(value)...)
}
default:
if value == nil {
buffer.WriteString(key)
} else {
if gstr.Pos(key, "?") == -1 {
if gstr.Pos(key, "<") == -1 && gstr.Pos(key, ">") == -1 && gstr.Pos(key, "=") == -1 {
buffer.WriteString(key + "=?")
} else {
buffer.WriteString(key + "?")
}
} else {
buffer.WriteString(key)
}
tmpArgs = append(tmpArgs, value)
}
}
}
default:
buffer.WriteString(gconv.String(where))
}
// 没有任何条件查询参数,直接返回
if buffer.Len() == 0 {
return "", args
}
newWhere = buffer.String()
tmpArgs = append(tmpArgs, args...)
// 查询条件参数处理主要处理slice参数类型
if len(tmpArgs) > 0 {
for index, arg := range tmpArgs {
rv := reflect.ValueOf(arg)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
// '?'占位符支持slice类型,
// 这里会将slice参数拆散并更新原有占位符'?'为多个'?',使用','符号连接。
case reflect.Slice: fallthrough
case reflect.Array:
for i := 0; i < rv.Len(); i++ {
newArgs = append(newArgs, rv.Index(i).Interface())
}
// counter用于匹配该参数的位置(与index对应)
counter := 0
newWhere, _ = gregex.ReplaceStringFunc(`\?`, newWhere, func(s string) string {
counter++
if counter == index + 1 {
return "?" + strings.Repeat(",?", rv.Len() - 1)
}
return s
})
default:
// 支持例如 Where/And/Or("uid", 1) 这种格式
if gstr.Pos(newWhere, "?") == -1 {
if gstr.Pos(newWhere, "<") == -1 && gstr.Pos(newWhere, ">") == -1 && gstr.Pos(newWhere, "=") == -1 {
newWhere += "=?"
} else {
newWhere += "?"
}
}
newArgs = append(newArgs, arg)
}
}
}
return
default:
buffer.WriteString(gconv.String(where))
}
// 没有任何条件查询参数,直接返回
if buffer.Len() == 0 {
return "", args
}
newWhere = buffer.String()
tmpArgs = append(tmpArgs, args...)
// 查询条件参数处理主要处理slice参数类型
if len(tmpArgs) > 0 {
for index, arg := range tmpArgs {
rv := reflect.ValueOf(arg)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
// '?'占位符支持slice类型,
// 这里会将slice参数拆散并更新原有占位符'?'为多个'?',使用','符号连接。
case reflect.Slice:
fallthrough
case reflect.Array:
for i := 0; i < rv.Len(); i++ {
newArgs = append(newArgs, rv.Index(i).Interface())
}
// counter用于匹配该参数的位置(与index对应)
counter := 0
newWhere, _ = gregex.ReplaceStringFunc(`\?`, newWhere, func(s string) string {
counter++
if counter == index+1 {
return "?" + strings.Repeat(",?", rv.Len()-1)
}
return s
})
default:
// 支持例如 Where/And/Or("uid", 1) 这种格式
if gstr.Pos(newWhere, "?") == -1 {
if gstr.Pos(newWhere, "<") == -1 && gstr.Pos(newWhere, ">") == -1 && gstr.Pos(newWhere, "=") == -1 {
newWhere += "=?"
} else {
newWhere += "?"
}
}
newArgs = append(newArgs, arg)
}
}
}
return
}
// 将预处理参数转换为底层数据库引擎支持的格式。
// 主要是判断参数是否为复杂数据类型,如果是,那么转换为基础类型。
func convertParam(value interface{}) interface{} {
rv := reflect.ValueOf(value)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
case reflect.Struct:
// 底层数据库引擎支持 time.Time/*time.Time 类型
if v, ok := value.(time.Time); ok {
if v.IsZero() {
return "null"
}
return value
}
if v, ok := value.(*time.Time); ok {
if v.IsZero() {
return ""
}
return value
}
return gconv.String(value)
}
return value
rv := reflect.ValueOf(value)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
case reflect.Struct:
// 底层数据库引擎支持 time.Time/*time.Time 类型
if v, ok := value.(time.Time); ok {
if v.IsZero() {
return "null"
}
return value
}
if v, ok := value.(*time.Time); ok {
if v.IsZero() {
return ""
}
return value
}
return gconv.String(value)
}
return value
}
// 打印SQL对象(仅在debug=true时有效)
func printSql(v *Sql) {
s := fmt.Sprintf("%s, %v, %s, %s, %d ms, %s", v.Sql, v.Args,
gtime.NewFromTimeStamp(v.Start).Format("Y-m-d H:i:s.u"),
gtime.NewFromTimeStamp(v.End).Format("Y-m-d H:i:s.u"),
v.End - v.Start,
v.Func,
)
if v.Error != nil {
s += "\nError: " + v.Error.Error()
glog.Backtrace(true, 2).Error(s)
} else {
glog.Debug(s)
}
s := fmt.Sprintf("%s, %v, %s, %s, %d ms, %s", v.Sql, v.Args,
gtime.NewFromTimeStamp(v.Start).Format("Y-m-d H:i:s.u"),
gtime.NewFromTimeStamp(v.End).Format("Y-m-d H:i:s.u"),
v.End-v.Start,
v.Func,
)
if v.Error != nil {
s += "\nError: " + v.Error.Error()
glog.Backtrace(true, 2).Error(s)
} else {
glog.Debug(s)
}
}
// 格式化错误信息
func formatError(err error, query string, args ...interface{}) error {
if err != nil {
errStr := fmt.Sprintf("DB ERROR: %s\n", err.Error())
errStr += fmt.Sprintf("DB QUERY: %s\n", query)
if len(args) > 0 {
errStr += fmt.Sprintf("DB PARAM: %v\n", args)
}
err = errors.New(errStr)
}
return err
if err != nil {
errStr := fmt.Sprintf("DB ERROR: %s\n", err.Error())
errStr += fmt.Sprintf("DB QUERY: %s\n", query)
if len(args) > 0 {
errStr += fmt.Sprintf("DB PARAM: %v\n", args)
}
err = errors.New(errStr)
}
return err
}
// 根据insert选项获得操作名称
func getInsertOperationByOption(option int) string {
operator := "INSERT"
switch option {
case OPTION_REPLACE:
operator = "REPLACE"
case OPTION_SAVE:
case OPTION_IGNORE:
operator = "INSERT IGNORE"
}
return operator
operator := "INSERT"
switch option {
case OPTION_REPLACE:
operator = "REPLACE"
case OPTION_SAVE:
case OPTION_IGNORE:
operator = "INSERT IGNORE"
}
return operator
}
// 将对象转换为map如果对象带有继承对象那么执行递归转换。
@ -207,30 +210,30 @@ func getInsertOperationByOption(option int) string {
func structToMap(obj interface{}) map[string]interface{} {
data := gconv.Map(obj)
for key, value := range data {
rv := reflect.ValueOf(value)
rv := reflect.ValueOf(value)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
case reflect.Struct:
// 底层数据库引擎支持 time.Time/*time.Time 类型
if _, ok := value.(time.Time); ok {
continue
}
if _, ok := value.(*time.Time); ok {
continue
}
// 如果执行String方法那么执行字符串转换
if s, ok := value.(apiString); ok {
data[key] = s.String()
continue
}
delete(data, key)
for k, v := range structToMap(value) {
data[k] = v
}
case reflect.Struct:
// 底层数据库引擎支持 time.Time/*time.Time 类型
if _, ok := value.(time.Time); ok {
continue
}
if _, ok := value.(*time.Time); ok {
continue
}
// 如果执行String方法那么执行字符串转换
if s, ok := value.(apiString); ok {
data[key] = s.String()
continue
}
delete(data, key)
for k, v := range structToMap(value) {
data[k] = v
}
}
}
return data
@ -239,4 +242,4 @@ func structToMap(obj interface{}) map[string]interface{} {
// 使用递归的方式将map键值对映射到struct对象上注意参数<pointer>是一个指向struct的指针。
func mapToStruct(data map[string]interface{}, pointer interface{}) error {
return gconv.StructDeep(data, pointer)
}
}

View File

@ -9,11 +9,11 @@
package gdb
import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/g/util/gconv"
"reflect"
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/g/util/gconv"
"reflect"
)
// 数据库链式操作模型对象
@ -35,51 +35,51 @@ type Model struct {
cacheEnabled bool // 当前SQL操作是否开启查询缓存功能
cacheTime int // 查询缓存时间
cacheName string // 查询缓存名称
safe bool // 当前模型是否运行安全模式(可修改当前模型,否则每一次链式操作都是返回新的模型对象)
safe bool // 当前模型是否运行安全模式(可修改当前模型,否则每一次链式操作都是返回新的模型对象)
}
// 链式操作,数据表字段,可支持多个表,以半角逗号连接
func (bs *dbBase) Table(tables string) (*Model) {
return &Model {
db : bs.db,
tablesInit : tables,
tables : tables,
fields : "*",
safe : false,
func (bs *dbBase) Table(tables string) *Model {
return &Model{
db: bs.db,
tablesInit: tables,
tables: tables,
fields: "*",
safe: false,
}
}
// 链式操作,数据表字段,可支持多个表,以半角逗号连接
func (bs *dbBase) From(tables string) (*Model) {
func (bs *dbBase) From(tables string) *Model {
return bs.db.Table(tables)
}
// (事务)链式操作,数据表字段,可支持多个表,以半角逗号连接
func (tx *TX) Table(tables string) (*Model) {
func (tx *TX) Table(tables string) *Model {
return &Model{
db : tx.db,
tx : tx,
tablesInit : tables,
tables : tables,
safe : false,
db: tx.db,
tx: tx,
tablesInit: tables,
tables: tables,
safe: false,
}
}
// (事务)链式操作,数据表字段,可支持多个表,以半角逗号连接
func (tx *TX) From(tables string) (*Model) {
func (tx *TX) From(tables string) *Model {
return tx.Table(tables)
}
// 克隆一个当前对象
func (md *Model) Clone() *Model {
newModel := (*Model)(nil)
newModel := (*Model)(nil)
if md.tx != nil {
newModel = md.tx.Table(md.tablesInit)
newModel = md.tx.Table(md.tablesInit)
} else {
newModel = md.db.Table(md.tablesInit)
newModel = md.db.Table(md.tablesInit)
}
*newModel = *md
return newModel
*newModel = *md
return newModel
}
// 标识当前对象运行安全模式(可被修改)。
@ -89,194 +89,196 @@ func (md *Model) Clone() *Model {
// 2. 当标识模型对象为可修改,那么在当前模型对象的所有链式操作均会影响下一次的链式操作,
// 即使是链式操作分开执行。
// 3. 大部分ORM框架默认模型对象是可修改的但是GF框架的ORM提供给开发者更灵活更安全的链式操作选项。
func (md *Model) Safe(safe...bool) *Model {
if len(safe) > 0 {
md.safe = safe[0]
} else {
md.safe = true
}
return md
func (md *Model) Safe(safe ...bool) *Model {
if len(safe) > 0 {
md.safe = safe[0]
} else {
md.safe = true
}
return md
}
// 返回操作的模型对象可能是当前对象也可能是新的克隆对象根据alterable决定。
func (md *Model) getModel() *Model {
if !md.safe {
return md
} else {
return md.Clone()
}
if !md.safe {
return md
} else {
return md.Clone()
}
}
// 链式操作,左联表
func (md *Model) LeftJoin(joinTable string, on string) (*Model) {
model := md.getModel()
model.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", joinTable, on)
func (md *Model) LeftJoin(joinTable string, on string) *Model {
model := md.getModel()
model.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", joinTable, on)
return model
}
// 链式操作,右联表
func (md *Model) RightJoin(joinTable string, on string) (*Model) {
model := md.getModel()
model.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", joinTable, on)
func (md *Model) RightJoin(joinTable string, on string) *Model {
model := md.getModel()
model.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", joinTable, on)
return model
}
// 链式操作,内联表
func (md *Model) InnerJoin(joinTable string, on string) (*Model) {
model := md.getModel()
model.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", joinTable, on)
func (md *Model) InnerJoin(joinTable string, on string) *Model {
model := md.getModel()
model.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", joinTable, on)
return model
}
// 链式操作,查询字段
func (md *Model) Fields(fields string) (*Model) {
model := md.getModel()
model.fields = fields
func (md *Model) Fields(fields string) *Model {
model := md.getModel()
model.fields = fields
return model
}
// 链式操作,过滤字段
func (md *Model) Filter() (*Model) {
model := md.getModel()
model.filter = true
return model
func (md *Model) Filter() *Model {
model := md.getModel()
model.filter = true
return model
}
// 链式操作condition支持string & gdb.Map.
// 注意多个Where调用时会自动转换为And条件调用。
func (md *Model) Where(where interface{}, args ...interface{}) (*Model) {
model := md.getModel()
if model.where != "" {
return md.And(where, args...)
}
newWhere, newArgs := formatCondition(where, args)
model.where = newWhere
model.whereArgs = newArgs
func (md *Model) Where(where interface{}, args ...interface{}) *Model {
model := md.getModel()
if model.where != "" {
return md.And(where, args...)
}
newWhere, newArgs := formatCondition(where, args)
model.where = newWhere
model.whereArgs = newArgs
return model
}
// 链式操作添加AND条件到Where中
func (md *Model) And(where interface{}, args ...interface{}) (*Model) {
model := md.getModel()
newWhere, newArgs := formatCondition(where, args)
if len(model.where) > 0 && model.where[0] == '(' {
model.where = fmt.Sprintf(`%s AND (%s)`, model.where, newWhere)
} else {
model.where = fmt.Sprintf(`(%s) AND (%s)`, model.where, newWhere)
}
model.whereArgs = append(model.whereArgs, newArgs...)
func (md *Model) And(where interface{}, args ...interface{}) *Model {
model := md.getModel()
newWhere, newArgs := formatCondition(where, args)
if len(model.where) > 0 && model.where[0] == '(' {
model.where = fmt.Sprintf(`%s AND (%s)`, model.where, newWhere)
} else {
model.where = fmt.Sprintf(`(%s) AND (%s)`, model.where, newWhere)
}
model.whereArgs = append(model.whereArgs, newArgs...)
return model
}
// 链式操作添加OR条件到Where中
func (md *Model) Or(where interface{}, args ...interface{}) (*Model) {
model := md.getModel()
newWhere, newArgs := formatCondition(where, args)
if len(model.where) > 0 && model.where[0] == '(' {
model.where = fmt.Sprintf(`%s OR (%s)`, model.where, newWhere)
} else {
model.where = fmt.Sprintf(`(%s) OR (%s)`, model.where, newWhere)
}
model.whereArgs = append(model.whereArgs, newArgs...)
func (md *Model) Or(where interface{}, args ...interface{}) *Model {
model := md.getModel()
newWhere, newArgs := formatCondition(where, args)
if len(model.where) > 0 && model.where[0] == '(' {
model.where = fmt.Sprintf(`%s OR (%s)`, model.where, newWhere)
} else {
model.where = fmt.Sprintf(`(%s) OR (%s)`, model.where, newWhere)
}
model.whereArgs = append(model.whereArgs, newArgs...)
return model
}
// 链式操作group by
func (md *Model) GroupBy(groupBy string) (*Model) {
model := md.getModel()
model.groupBy = groupBy
func (md *Model) GroupBy(groupBy string) *Model {
model := md.getModel()
model.groupBy = groupBy
return model
}
// 链式操作order by
func (md *Model) OrderBy(orderBy string) (*Model) {
model := md.getModel()
model.orderBy = orderBy
func (md *Model) OrderBy(orderBy string) *Model {
model := md.getModel()
model.orderBy = orderBy
return model
}
// 链式操作limit
func (md *Model) Limit(start int, limit int) (*Model) {
model := md.getModel()
model.start = start
model.limit = limit
func (md *Model) Limit(start int, limit int) *Model {
model := md.getModel()
model.start = start
model.limit = limit
return model
}
// 链式操作翻页注意分页页码从1开始而Limit方法从0开始。
func (md *Model) ForPage(page, limit int) (*Model) {
model := md.getModel()
model.start = (page - 1) * limit
model.limit = limit
func (md *Model) ForPage(page, limit int) *Model {
model := md.getModel()
model.start = (page - 1) * limit
model.limit = limit
return model
}
// 设置批处理的大小
func (md *Model) Batch(batch int) *Model {
model := md.getModel()
model.batch = batch
return model
model := md.getModel()
model.batch = batch
return model
}
// 查询缓存/清除缓存操作,需要注意的是,事务查询不支持缓存。
// 当time < 0时表示清除缓存 time=0时表示不过期, time > 0时表示过期时间time过期时间单位
// name表示自定义的缓存名称便于业务层精准定位缓存项(如果业务层需要手动清理时,必须指定缓存名称)
// 例如:查询缓存时设置名称,清理缓存时可以给定清理的缓存名称进行精准清理。
func (md *Model) Cache(time int, name ... string) *Model {
model := md.getModel()
model.cacheTime = time
if len(name) > 0 {
model.cacheName = name[0]
}
// 查询缓存特性不支持事务操作
if model.tx == nil {
model.cacheEnabled = true
}
return model
func (md *Model) Cache(time int, name ...string) *Model {
model := md.getModel()
model.cacheTime = time
if len(name) > 0 {
model.cacheName = name[0]
}
// 查询缓存特性不支持事务操作
if model.tx == nil {
model.cacheEnabled = true
}
return model
}
// 链式操作操作数据项参数data类型支持 string/map/slice/struct/*struct ,
// 也可以是key,value,key,value,...。
func (md *Model) Data(data ...interface{}) *Model {
model := md.getModel()
model := md.getModel()
if len(data) > 1 {
m := make(map[string]interface{})
for i := 0; i < len(data); i += 2 {
m[gconv.String(data[i])] = data[i + 1]
m[gconv.String(data[i])] = data[i+1]
}
model.data = m
model.data = m
} else {
switch params := data[0].(type) {
case Result:
model.data = params.ToList()
case Record:
model.data = params.ToMap()
case List:
model.data = params
case Map:
model.data = params
case Result:
model.data = params.ToList()
case Record:
model.data = params.ToMap()
case List:
model.data = params
case Map:
model.data = params
default:
rv := reflect.ValueOf(params)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
// 如果是slice那么转换为List类型
case reflect.Slice:
fallthrough
case reflect.Array:
list := make(List, rv.Len())
for i := 0; i < rv.Len(); i++ {
list[i] = structToMap(rv.Index(i).Interface())
}
model.data = list
case reflect.Map:
fallthrough
case reflect.Struct:
model.data = Map(structToMap(data[0]))
default:
rv := reflect.ValueOf(params)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
// 如果是slice那么转换为List类型
case reflect.Slice: fallthrough
case reflect.Array:
list := make(List, rv.Len())
for i := 0; i < rv.Len(); i++ {
list[i] = structToMap(rv.Index(i).Interface())
}
model.data = list
case reflect.Map: fallthrough
case reflect.Struct:
model.data = Map(structToMap(data[0]))
default:
model.data = data[0]
}
model.data = data[0]
}
}
}
return model
@ -301,19 +303,19 @@ func (md *Model) Insert() (result sql.Result, err error) {
batch = md.batch
}
if md.filter {
for k, m := range list {
list[k] = md.db.filterFields(md.tables, m)
}
}
for k, m := range list {
list[k] = md.db.filterFields(md.tables, m)
}
}
if md.tx == nil {
return md.db.BatchInsert(md.tables, list, batch)
} else {
return md.tx.BatchInsert(md.tables, list, batch)
}
} else if data, ok := md.data.(Map); ok {
if md.filter {
data = md.db.filterFields(md.tables, data)
}
if md.filter {
data = md.db.filterFields(md.tables, data)
}
if md.tx == nil {
return md.db.Insert(md.tables, data)
} else {
@ -341,20 +343,20 @@ func (md *Model) Replace() (result sql.Result, err error) {
if md.batch > 0 {
batch = md.batch
}
if md.filter {
for k, m := range list {
list[k] = md.db.filterFields(md.tables, m)
}
}
if md.filter {
for k, m := range list {
list[k] = md.db.filterFields(md.tables, m)
}
}
if md.tx == nil {
return md.db.BatchReplace(md.tables, list, batch)
} else {
return md.tx.BatchReplace(md.tables, list, batch)
}
} else if data, ok := md.data.(Map); ok {
if md.filter {
data = md.db.filterFields(md.tables, data)
}
if md.filter {
data = md.db.filterFields(md.tables, data)
}
if md.tx == nil {
return md.db.Replace(md.tables, data)
} else {
@ -382,20 +384,20 @@ func (md *Model) Save() (result sql.Result, err error) {
if md.batch > 0 {
batch = md.batch
}
if md.filter {
for k, m := range list {
list[k] = md.db.filterFields(md.tables, m)
}
}
if md.filter {
for k, m := range list {
list[k] = md.db.filterFields(md.tables, m)
}
}
if md.tx == nil {
return md.db.BatchSave(md.tables, list, batch)
} else {
return md.tx.BatchSave(md.tables, list, batch)
}
} else if data, ok := md.data.(Map); ok {
if md.filter {
data = md.db.filterFields(md.tables, data)
}
if md.filter {
data = md.db.filterFields(md.tables, data)
}
if md.tx == nil {
return md.db.Save(md.tables, data)
} else {
@ -415,17 +417,17 @@ func (md *Model) Update() (result sql.Result, err error) {
if md.data == nil {
return nil, errors.New("updating table with empty data")
}
if md.filter {
if data, ok := md.data.(Map); ok {
if md.filter {
md.data = md.db.filterFields(md.tables, data)
}
}
}
if md.filter {
if data, ok := md.data.(Map); ok {
if md.filter {
md.data = md.db.filterFields(md.tables, data)
}
}
}
if md.tx == nil {
return md.db.doUpdate(nil, md.tables, md.data, md.where, md.whereArgs ...)
return md.db.doUpdate(nil, md.tables, md.data, md.where, md.whereArgs...)
} else {
return md.tx.doUpdate(md.tables, md.data, md.where, md.whereArgs ...)
return md.tx.doUpdate(md.tables, md.data, md.where, md.whereArgs...)
}
}
@ -499,34 +501,34 @@ func (md *Model) Structs(objPointerSlice interface{}) error {
// 参数应该为指针类型,否则返回失败。
// 该方法自动识别参数类型调用Struct/Structs方法。
func (md *Model) Scan(objPointer interface{}) error {
t := reflect.TypeOf(objPointer)
k := t.Kind()
if k != reflect.Ptr {
return fmt.Errorf("params should be type of pointer, but got: %v", k)
}
k = t.Elem().Kind()
switch k {
case reflect.Array:
case reflect.Slice:
return md.Structs(objPointer)
case reflect.Struct:
return md.Struct(objPointer)
default:
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
}
return nil
t := reflect.TypeOf(objPointer)
k := t.Kind()
if k != reflect.Ptr {
return fmt.Errorf("params should be type of pointer, but got: %v", k)
}
k = t.Elem().Kind()
switch k {
case reflect.Array:
case reflect.Slice:
return md.Structs(objPointer)
case reflect.Struct:
return md.Struct(objPointer)
default:
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
}
return nil
}
// 链式操作查询数量fields可以为空也可以自定义查询字段
// 当给定自定义查询字段时该字段必须为数量结果否则会引起歧义使用如md.Fields("COUNT(id)")
func (md *Model) Count() (int, error) {
defer func(fields string) {
md.fields = fields
}(md.fields)
defer func(fields string) {
md.fields = fields
}(md.fields)
if md.fields == "" || md.fields == "*" {
md.fields = "COUNT(1)"
} else {
md.fields = fmt.Sprintf(`COUNT(%s)`, md.fields)
md.fields = fmt.Sprintf(`COUNT(%s)`, md.fields)
}
s := md.getFormattedSql()
if len(md.groupBy) > 0 {
@ -604,10 +606,10 @@ func (md *Model) getFormattedSql() string {
// 组块结果集。
func (md *Model) Chunk(limit int, callback func(result Result, err error) bool) {
page := 1
page := 1
model := md
for {
model = model.ForPage(page, limit)
model = model.ForPage(page, limit)
data, err := model.All()
if err != nil {
callback(nil, err)

View File

@ -83,69 +83,69 @@ func (db *dbMssql) parseSql(sql string) string {
index++
switch keyword {
case "SELECT":
//不含LIMIT关键字则不处理
if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) {
case "SELECT":
//不含LIMIT关键字则不处理
if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) {
break
}
//不含LIMIT则不处理
if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false {
break
}
//判断SQL中是否含有order by
selectStr := ""
orderbyStr := ""
haveOrderby := gregex.IsMatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql)
if haveOrderby {
//取order by 前面的字符串
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "ORDER BY") == false {
break
}
selectStr = queryExpr[2]
//不含LIMIT则不处理
if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false {
//取order by表达式的值
orderbyExpr, _ := gregex.MatchString("((?i)ORDER BY)(.+)((?i)LIMIT)", sql)
if len(orderbyExpr) != 4 || strings.EqualFold(orderbyExpr[1], "ORDER BY") == false || strings.EqualFold(orderbyExpr[3], "LIMIT") == false {
break
}
orderbyStr = orderbyExpr[2]
} else {
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false {
break
}
selectStr = queryExpr[2]
}
//判断SQL中是否含有order by
selectStr := ""
orderbyStr := ""
haveOrderby := gregex.IsMatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql)
if haveOrderby {
//取order by 前面的字符串
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql)
//取limit后面的取值范围
first, limit := 0, 0
for i := 1; i < len(res[index]); i++ {
if len(strings.TrimSpace(res[index][i])) == 0 {
continue
}
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "ORDER BY") == false {
break
}
selectStr = queryExpr[2]
if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") {
first, _ = strconv.Atoi(res[index][i+1])
limit, _ = strconv.Atoi(res[index][i+2])
break
}
}
//取order by表达式的值
orderbyExpr, _ := gregex.MatchString("((?i)ORDER BY)(.+)((?i)LIMIT)", sql)
if len(orderbyExpr) != 4 || strings.EqualFold(orderbyExpr[1], "ORDER BY") == false || strings.EqualFold(orderbyExpr[3], "LIMIT") == false {
break
}
orderbyStr = orderbyExpr[2]
if haveOrderby {
sql = fmt.Sprintf("SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROWNUMBER_, %s ) as TMP_ WHERE TMP_.ROWNUMBER_ > %d AND TMP_.ROWNUMBER_ <= %d", orderbyStr, selectStr, first, limit)
} else {
if first == 0 {
first = limit
} else {
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false {
break
}
selectStr = queryExpr[2]
first = limit - first
}
//取limit后面的取值范围
first, limit := 0, 0
for i := 1; i < len(res[index]); i++ {
if len(strings.TrimSpace(res[index][i])) == 0 {
continue
}
if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") {
first, _ = strconv.Atoi(res[index][i+1])
limit, _ = strconv.Atoi(res[index][i+2])
break
}
}
if haveOrderby {
sql = fmt.Sprintf("SELECT * FROM (SELECT ROW_NUMBER() OVER (ORDER BY %s) as ROWNUMBER_, %s ) as TMP_ WHERE TMP_.ROWNUMBER_ > %d AND TMP_.ROWNUMBER_ <= %d", orderbyStr, selectStr, first, limit)
} else {
if first == 0 {
first = limit
} else {
first = limit - first
}
sql = fmt.Sprintf("SELECT * FROM (SELECT TOP %d * FROM (SELECT TOP %d %s) as TMP1_ ) as TMP2_ ", first, limit, selectStr)
}
default:
sql = fmt.Sprintf("SELECT * FROM (SELECT TOP %d * FROM (SELECT TOP %d %s) as TMP1_ ) as TMP2_ ", first, limit, selectStr)
}
default:
}
return sql
}

View File

@ -4,7 +4,6 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gdb
import (
@ -15,31 +14,31 @@ import (
// 数据库链接对象
type dbMysql struct {
*dbBase
*dbBase
}
// 创建SQL操作对象内部采用了lazy link处理
func (db *dbMysql) Open (config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)
}
if db, err := sql.Open("gf-mysql", source); err == nil {
return db, nil
} else {
return nil, err
}
func (db *dbMysql) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true",
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset)
}
if db, err := sql.Open("gf-mysql", source); err == nil {
return db, nil
} else {
return nil, err
}
}
// 获得关键字操作符
func (db *dbMysql) getChars () (charLeft string, charRight string) {
return "`", "`"
func (db *dbMysql) getChars() (charLeft string, charRight string) {
return "`", "`"
}
// 在执行sql之前对sql进行进一步处理
func (db *dbMysql) handleSqlBeforeExec(query string) string {
return query
}
return query
}

View File

@ -75,70 +75,70 @@ func (db *dbOracle) parseSql(sql string) string {
return ""
}
index := 0
index := 0
keyword := strings.TrimSpace(res[index][0])
keyword = strings.ToUpper(keyword)
keyword = strings.ToUpper(keyword)
index++
switch keyword {
case "SELECT":
//不含LIMIT关键字则不处理
if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) {
case "SELECT":
//不含LIMIT关键字则不处理
if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) {
break
}
//取limit前面的字符串
if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false {
break
}
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false {
break
}
//取limit后面的取值范围
first, limit := 0, 0
for i := 1; i < len(res[index]); i++ {
if len(strings.TrimSpace(res[index][i])) == 0 {
continue
}
if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") {
first, _ = strconv.Atoi(res[index][i+1])
limit, _ = strconv.Atoi(res[index][i+2])
break
}
}
//取limit前面的字符串
if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false {
break
}
//也可以使用between,据说这种写法的性能会比between好点,里层SQL中的ROWNUM_ >= limit可以缩小查询后的数据集规模
sql = fmt.Sprintf("SELECT * FROM (SELECT GFORM.*, ROWNUM ROWNUM_ FROM (%s %s) GFORM WHERE ROWNUM <= %d) WHERE ROWNUM_ >= %d", queryExpr[1], queryExpr[2], limit, first)
case "INSERT":
//获取VALUE的值匹配所有带括号的值,会将INSERT INTO后的值匹配到所以下面的判断语句会判断数组长度是否小于3
valueExpr, err := gregex.MatchAllString(`(\s*\(([^\(\)]*)\))`, sql)
if err != nil {
return sql
}
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false {
break
}
//判断VALUE后的值是否有多个只有在批量插入的时候才需要做转换如只有1个VALUE则不需要做转换
if len(valueExpr) < 3 {
break
}
//取limit后面的取值范围
first, limit := 0, 0
for i := 1; i < len(res[index]); i++ {
if len(strings.TrimSpace(res[index][i])) == 0 {
continue
}
//获取INTO后面的值
tableExpr, err := gregex.MatchString(`(?i)\s*(INTO\s+\w+\(([^\(\)]*)\))`, sql)
if err != nil {
return sql
}
tableExpr[0] = strings.TrimSpace(tableExpr[0])
if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") {
first, _ = strconv.Atoi(res[index][i+1])
limit, _ = strconv.Atoi(res[index][i+2])
break
}
}
sql = "INSERT ALL"
for i := 1; i < len(valueExpr); i++ {
sql += fmt.Sprintf(" %s VALUES%s", tableExpr[0], strings.TrimSpace(valueExpr[i][0]))
}
sql += " SELECT 1 FROM DUAL"
//也可以使用between,据说这种写法的性能会比between好点,里层SQL中的ROWNUM_ >= limit可以缩小查询后的数据集规模
sql = fmt.Sprintf("SELECT * FROM (SELECT GFORM.*, ROWNUM ROWNUM_ FROM (%s %s) GFORM WHERE ROWNUM <= %d) WHERE ROWNUM_ >= %d", queryExpr[1], queryExpr[2], limit, first)
case "INSERT":
//获取VALUE的值匹配所有带括号的值,会将INSERT INTO后的值匹配到所以下面的判断语句会判断数组长度是否小于3
valueExpr, err := gregex.MatchAllString(`(\s*\(([^\(\)]*)\))`, sql)
if err != nil {
return sql
}
//判断VALUE后的值是否有多个只有在批量插入的时候才需要做转换如只有1个VALUE则不需要做转换
if len(valueExpr) < 3 {
break
}
//获取INTO后面的值
tableExpr, err := gregex.MatchString(`(?i)\s*(INTO\s+\w+\(([^\(\)]*)\))`, sql)
if err != nil {
return sql
}
tableExpr[0] = strings.TrimSpace(tableExpr[0])
sql = "INSERT ALL"
for i := 1; i < len(valueExpr); i++ {
sql += fmt.Sprintf(" %s VALUES%s", tableExpr[0], strings.TrimSpace(valueExpr[i][0]))
}
sql += " SELECT 1 FROM DUAL"
default:
default:
}
return sql
}

View File

@ -4,13 +4,12 @@
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gdb
import (
"fmt"
"regexp"
"database/sql"
"database/sql"
"fmt"
"regexp"
)
// PostgreSQL的适配.
@ -20,36 +19,36 @@ import (
// 数据库链接对象
type dbPgsql struct {
*dbBase
*dbBase
}
// 创建SQL操作对象内部采用了lazy link处理
func (db *dbPgsql) Open (config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s", config.User, config.Pass, config.Host, config.Port, config.Name)
}
if db, err := sql.Open("postgres", source); err == nil {
return db, nil
} else {
return nil, err
}
func (db *dbPgsql) Open(config *ConfigNode) (*sql.DB, error) {
var source string
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
source = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s", config.User, config.Pass, config.Host, config.Port, config.Name)
}
if db, err := sql.Open("postgres", source); err == nil {
return db, nil
} else {
return nil, err
}
}
// 获得关键字操作符
func (db *dbPgsql) getChars () (charLeft string, charRight string) {
return "\"", "\""
func (db *dbPgsql) getChars() (charLeft string, charRight string) {
return "\"", "\""
}
// 在执行sql之前对sql进行进一步处理
func (db *dbPgsql) handleSqlBeforeExec(query string) string {
reg := regexp.MustCompile("\\?")
index := 0
str := reg.ReplaceAllStringFunc(query, func (s string) string {
index ++
return fmt.Sprintf("$%d", index)
})
return str
}
reg := regexp.MustCompile("\\?")
index := 0
str := reg.ReplaceAllStringFunc(query, func(s string) string {
index++
return fmt.Sprintf("$%d", index)
})
return str
}

View File

@ -37,7 +37,7 @@ func (db *dbSqlite) Open(config *ConfigNode) (*sql.DB, error) {
}
// 获得关键字操作符
func (db *dbSqlite) getChars () (charLeft string, charRight string) {
func (db *dbSqlite) getChars() (charLeft string, charRight string) {
return "`", "`"
}
@ -46,4 +46,4 @@ func (db *dbSqlite) getChars () (charLeft string, charRight string) {
// @todo 将ON DUPLICATE KEY UPDATE触发器修改为两条SQL语句(INSERT OR IGNORE & UPDATE)
func (db *dbSqlite) handleSqlBeforeExec(query string) string {
return query
}
}

View File

@ -7,10 +7,10 @@
package gdb
import (
"fmt"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gregex"
"strings"
"fmt"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/util/gconv"
"strings"
)
/*
@ -25,80 +25,80 @@ func (bs *dbBase) syncTableStructure() {
// 字段类型转换将数据库字段类型转换为golang变量类型
func (bs *dbBase) convertValue(fieldValue interface{}, fieldType string) interface{} {
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
t = strings.ToLower(t)
switch t {
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
return gconv.Bytes(fieldValue)
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
t = strings.ToLower(t)
switch t {
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
return gconv.Bytes(fieldValue)
case "bit", "int", "tinyint", "small_int", "medium_int":
return gconv.Int(fieldValue)
case "bit", "int", "tinyint", "small_int", "medium_int":
return gconv.Int(fieldValue)
case "big_int":
return gconv.Int64(fieldValue)
case "big_int":
return gconv.Int64(fieldValue)
case "float", "double", "decimal":
return gconv.Float64(fieldValue)
case "float", "double", "decimal":
return gconv.Float64(fieldValue)
case "bool":
return gconv.Bool(fieldValue)
case "bool":
return gconv.Bool(fieldValue)
default:
// 自动识别类型, 以便默认支持更多数据库类型
switch {
case strings.Contains(t, "int"):
return gconv.Int(fieldValue)
default:
// 自动识别类型, 以便默认支持更多数据库类型
switch {
case strings.Contains(t, "int"):
return gconv.Int(fieldValue)
case strings.Contains(t, "text") || strings.Contains(t, "char"):
return gconv.String(fieldValue)
case strings.Contains(t, "text") || strings.Contains(t, "char"):
return gconv.String(fieldValue)
case strings.Contains(t, "float") || strings.Contains(t, "double"):
return gconv.Float64(fieldValue)
case strings.Contains(t, "float") || strings.Contains(t, "double"):
return gconv.Float64(fieldValue)
case strings.Contains(t, "bool"):
return gconv.Bool(fieldValue)
case strings.Contains(t, "bool"):
return gconv.Bool(fieldValue)
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
return gconv.Bytes(fieldValue)
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
return gconv.Bytes(fieldValue)
default:
return gconv.String(fieldValue)
}
}
default:
return gconv.String(fieldValue)
}
}
}
// 将map的数据按照fields进行过滤只保留与表字段同名的数据
func (bs *dbBase) filterFields(table string, data map[string]interface{}) map[string]interface{} {
if fields, err := bs.db.getTableFields(table); err == nil {
for k, _ := range data {
if _, ok := fields[k]; !ok {
delete(data, k)
}
}
}
return data
if fields, err := bs.db.getTableFields(table); err == nil {
for k, _ := range data {
if _, ok := fields[k]; !ok {
delete(data, k)
}
}
}
return data
}
// 获得指定表表的数据结构构造成map哈希表返回其中键名为表字段名称键值暂无用途(默认为字段数据类型).
func (bs *dbBase) getTableFields(table string) (fields map[string]string, err error) {
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
v := bs.cache.GetOrSetFunc("table_fields_" + table, func() interface{} {
result := (Result)(nil)
charL, charR := bs.db.getChars()
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s%s%s`, charL, table, charR))
if err != nil {
return nil
}
fields = make(map[string]string)
for _, m := range result {
fields[m["Field"].String()] = m["Type"].String()
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]string)
}
return
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
v := bs.cache.GetOrSetFunc("table_fields_"+table, func() interface{} {
result := (Result)(nil)
charL, charR := bs.db.getChars()
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s%s%s`, charL, table, charR))
if err != nil {
return nil
}
fields = make(map[string]string)
for _, m := range result {
fields[m["Field"].String()] = m["Type"].String()
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]string)
}
return
}
/*
@ -116,4 +116,4 @@ func (bs *dbBase) getTables() []string {
}
return nil
}
*/
*/

View File

@ -7,182 +7,179 @@
package gdb
import (
"database/sql"
"fmt"
"github.com/gogf/gf/g/text/gregex"
"reflect"
"database/sql"
"fmt"
"github.com/gogf/gf/g/text/gregex"
"reflect"
)
// 数据库事务对象
type TX struct {
db DB
tx *sql.Tx
master *sql.DB
db DB
tx *sql.Tx
master *sql.DB
}
// 事务操作,提交
func (tx *TX) Commit() error {
return tx.tx.Commit()
return tx.tx.Commit()
}
// 事务操作,回滚
func (tx *TX) Rollback() error {
return tx.tx.Rollback()
return tx.tx.Rollback()
}
// (事务)数据库sql查询操作主要执行查询
func (tx *TX) Query(query string, args ...interface{}) (rows *sql.Rows, err error) {
return tx.db.doQuery(tx.tx, query, args...)
return tx.db.doQuery(tx.tx, query, args...)
}
// (事务)执行一条sql并返回执行情况主要用于非查询操作
func (tx *TX) Exec(query string, args ...interface{}) (sql.Result, error) {
return tx.db.doExec(tx.tx, query, args...)
return tx.db.doExec(tx.tx, query, args...)
}
// sql预处理执行完成后调用返回值sql.Stmt.Exec完成sql操作
func (tx *TX) Prepare(query string) (*sql.Stmt, error) {
return tx.db.doPrepare(tx.tx, query)
return tx.db.doPrepare(tx.tx, query)
}
// 数据库查询,获取查询结果集,以列表结构返回
func (tx *TX) GetAll(query string, args ...interface{}) (Result, error) {
rows, err := tx.Query(query, args ...)
if err != nil || rows == nil {
return nil, err
}
defer rows.Close()
return tx.db.rowsToResult(rows)
rows, err := tx.Query(query, args...)
if err != nil || rows == nil {
return nil, err
}
defer rows.Close()
return tx.db.rowsToResult(rows)
}
// 数据库查询,获取查询结果记录,以关联数组结构返回
func (tx *TX) GetOne(query string, args ...interface{}) (Record, error) {
list, err := tx.GetAll(query, args ...)
if err != nil {
return nil, err
}
if len(list) > 0 {
return list[0], nil
}
return nil, nil
list, err := tx.GetAll(query, args...)
if err != nil {
return nil, err
}
if len(list) > 0 {
return list[0], nil
}
return nil, nil
}
// 数据库查询获取查询结果记录自动映射数据到给定的struct对象中
func (tx *TX) GetStruct(obj interface{}, query string, args ...interface{}) error {
one, err := tx.GetOne(query, args...)
if err != nil {
return err
}
return one.ToStruct(obj)
one, err := tx.GetOne(query, args...)
if err != nil {
return err
}
return one.ToStruct(obj)
}
// 数据库查询查询多条记录并自动转换为指定的slice对象, 如: []struct/[]*struct。
func (tx *TX) GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error {
all, err := tx.GetAll(query, args...)
if err != nil {
return err
}
return all.ToStructs(objPointerSlice)
all, err := tx.GetAll(query, args...)
if err != nil {
return err
}
return all.ToStructs(objPointerSlice)
}
// 将结果转换为指定的struct/*struct/[]struct/[]*struct,
// 参数应该为指针类型,否则返回失败。
// 该方法自动识别参数类型调用Struct/Structs方法。
func (tx *TX) GetScan(objPointer interface{}, query string, args ...interface{}) error {
t := reflect.TypeOf(objPointer)
k := t.Kind()
if k != reflect.Ptr {
return fmt.Errorf("params should be type of pointer, but got: %v", k)
}
k = t.Elem().Kind()
switch k {
case reflect.Array:
case reflect.Slice:
return tx.db.GetStructs(objPointer, query, args ...)
case reflect.Struct:
return tx.db.GetStruct(objPointer, query, args ...)
default:
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
}
return nil
t := reflect.TypeOf(objPointer)
k := t.Kind()
if k != reflect.Ptr {
return fmt.Errorf("params should be type of pointer, but got: %v", k)
}
k = t.Elem().Kind()
switch k {
case reflect.Array:
case reflect.Slice:
return tx.db.GetStructs(objPointer, query, args...)
case reflect.Struct:
return tx.db.GetStruct(objPointer, query, args...)
default:
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
}
return nil
}
// 数据库查询,获取查询字段值
func (tx *TX) GetValue(query string, args ...interface{}) (Value, error) {
one, err := tx.GetOne(query, args ...)
if err != nil {
return nil, err
}
for _, v := range one {
return v, nil
}
return nil, nil
one, err := tx.GetOne(query, args...)
if err != nil {
return nil, err
}
for _, v := range one {
return v, nil
}
return nil, nil
}
// 数据库查询,获取查询数量
func (tx *TX) GetCount(query string, args ...interface{}) (int, error) {
if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, query) {
query, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, query)
}
value, err := tx.GetValue(query, args ...)
if err != nil {
return 0, err
}
return value.Int(), nil
if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, query) {
query, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, query)
}
value, err := tx.GetValue(query, args...)
if err != nil {
return 0, err
}
return value.Int(), nil
}
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
func (tx *TX) Insert(table string, data interface{}, batch...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, OPTION_INSERT, batch...)
func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, OPTION_INSERT, batch...)
}
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
func (tx *TX) Replace(table string, data interface{}, batch...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, OPTION_REPLACE, batch...)
func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, OPTION_REPLACE, batch...)
}
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
func (tx *TX) Save(table string, data interface{}, batch...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, OPTION_SAVE, batch...)
func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, error) {
return tx.db.doInsert(tx.tx, table, data, OPTION_SAVE, batch...)
}
// CURD操作:批量数据指定批次量写入
func (tx *TX) BatchInsert(table string, list interface{}, batch...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_INSERT, batch...)
func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_INSERT, batch...)
}
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
func (tx *TX) BatchReplace(table string, list interface{}, batch...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_REPLACE, batch...)
func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_REPLACE, batch...)
}
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
func (tx *TX) BatchSave(table string, list interface{}, batch...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_SAVE, batch...)
func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_SAVE, batch...)
}
// CURD操作:数据更新统一采用sql预处理,
// data参数支持字符串或者关联数组类型内部会自行做判断处理.
func (tx *TX) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := formatCondition(condition, args)
return tx.doUpdate(table, data, newWhere, newArgs ...)
newWhere, newArgs := formatCondition(condition, args)
return tx.doUpdate(table, data, newWhere, newArgs...)
}
// 与Update方法的区别是不处理条件参数
func (tx *TX) doUpdate(table string, data interface{}, condition string, args ...interface{}) (sql.Result, error) {
return tx.db.doUpdate(tx.tx, table, data, condition, args ...)
return tx.db.doUpdate(tx.tx, table, data, condition, args...)
}
// CURD操作:删除数据
func (tx *TX) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := formatCondition(condition, args)
return tx.doDelete(table, newWhere, newArgs ...)
newWhere, newArgs := formatCondition(condition, args)
return tx.doDelete(table, newWhere, newArgs...)
}
// 与Delete方法的区别是不处理条件参数
func (tx *TX) doDelete(table string, condition string, args ...interface{}) (sql.Result, error) {
return tx.db.doDelete(tx.tx, table, condition, args ...)
return tx.db.doDelete(tx.tx, table, condition, args...)
}

View File

@ -12,26 +12,26 @@ import (
// 将记录结果转换为JSON字符串
func (r Record) ToJson() string {
content, _ := gparser.VarToJson(r.ToMap())
return string(content)
content, _ := gparser.VarToJson(r.ToMap())
return string(content)
}
// 将记录结果转换为XML字符串
func (r Record) ToXml(rootTag...string) string {
content, _ := gparser.VarToXml(r.ToMap(), rootTag...)
return string(content)
func (r Record) ToXml(rootTag ...string) string {
content, _ := gparser.VarToXml(r.ToMap(), rootTag...)
return string(content)
}
// 将Record转换为Map其中最主要的区别是里面的键值被强制转换为string类型方便json处理
func (r Record) ToMap() Map {
m := make(map[string]interface{})
for k, v := range r {
m[k] = v.Val()
}
return m
m := make(map[string]interface{})
for k, v := range r {
m[k] = v.Val()
}
return m
}
// 将Map变量映射到指定的struct对象中注意参数应当是一个对象的指针
func (r Record) ToStruct(pointer interface{}) error {
return mapToStruct(r.ToMap(), pointer)
return mapToStruct(r.ToMap(), pointer)
}

View File

@ -7,121 +7,121 @@
package gdb
import (
"fmt"
"github.com/gogf/gf/g/encoding/gparser"
"reflect"
"fmt"
"github.com/gogf/gf/g/encoding/gparser"
"reflect"
)
// 将结果集转换为JSON字符串
func (r Result) ToJson() string {
content, _ := gparser.VarToJson(r.ToList())
return string(content)
content, _ := gparser.VarToJson(r.ToList())
return string(content)
}
// 将结果集转换为XML字符串
func (r Result) ToXml(rootTag...string) string {
content, _ := gparser.VarToXml(r.ToList(), rootTag...)
return string(content)
func (r Result) ToXml(rootTag ...string) string {
content, _ := gparser.VarToXml(r.ToList(), rootTag...)
return string(content)
}
// 将结果集转换为List类型返回便于json处理
func (r Result) ToList() List {
l := make(List, len(r))
for k, v := range r {
l[k] = v.ToMap()
}
return l
l := make(List, len(r))
for k, v := range r {
l[k] = v.ToMap()
}
return l
}
// 将结果列表按照指定的字段值做map[string]Map
func (r Result) ToStringMap(key string) map[string]Map {
m := make(map[string]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.String()] = item.ToMap()
}
}
return m
m := make(map[string]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.String()] = item.ToMap()
}
}
return m
}
// 将结果列表按照指定的字段值做map[int]Map
func (r Result) ToIntMap(key string) map[int]Map {
m := make(map[int]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Int()] = item.ToMap()
}
}
return m
m := make(map[int]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Int()] = item.ToMap()
}
}
return m
}
// 将结果列表按照指定的字段值做map[uint]Map
func (r Result) ToUintMap(key string) map[uint]Map {
m := make(map[uint]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Uint()] = item.ToMap()
}
}
return m
m := make(map[uint]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Uint()] = item.ToMap()
}
}
return m
}
// 将结果列表按照指定的字段值做map[string]Record
func (r Result) ToStringRecord(key string) map[string]Record {
m := make(map[string]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.String()] = item
}
}
return m
m := make(map[string]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.String()] = item
}
}
return m
}
// 将结果列表按照指定的字段值做map[int]Record
func (r Result) ToIntRecord(key string) map[int]Record {
m := make(map[int]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Int()] = item
}
}
return m
m := make(map[int]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Int()] = item
}
}
return m
}
// 将结果列表按照指定的字段值做map[uint]Record
func (r Result) ToUintRecord(key string) map[uint]Record {
m := make(map[uint]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Uint()] = item
}
}
return m
m := make(map[uint]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Uint()] = item
}
}
return m
}
// 将结果列表转换为指定对象的slice。
func (r Result) ToStructs(objPointerSlice interface{}) error {
l := len(r)
if l == 0 {
return nil
}
t := reflect.TypeOf(objPointerSlice)
if t.Kind() != reflect.Ptr {
return fmt.Errorf("params should be type of pointer, but got: %v", t.Kind())
}
a := reflect.MakeSlice(t.Elem(), l, l)
itemType := a.Index(0).Type()
for i := 0; i < l; i++ {
if itemType.Kind() == reflect.Ptr {
e := reflect.New(itemType.Elem()).Elem()
r[i].ToStruct(e)
a.Index(i).Set(e.Addr())
} else {
e := reflect.New(itemType).Elem()
r[i].ToStruct(e)
a.Index(i).Set(e)
}
}
reflect.ValueOf(objPointerSlice).Elem().Set(a)
return nil
l := len(r)
if l == 0 {
return nil
}
t := reflect.TypeOf(objPointerSlice)
if t.Kind() != reflect.Ptr {
return fmt.Errorf("params should be type of pointer, but got: %v", t.Kind())
}
a := reflect.MakeSlice(t.Elem(), l, l)
itemType := a.Index(0).Type()
for i := 0; i < l; i++ {
if itemType.Kind() == reflect.Ptr {
e := reflect.New(itemType.Elem()).Elem()
r[i].ToStruct(e)
a.Index(i).Set(e.Addr())
} else {
e := reflect.New(itemType).Elem()
r[i].ToStruct(e)
a.Index(i).Set(e)
}
}
reflect.ValueOf(objPointerSlice).Elem().Set(a)
return nil
}

View File

@ -7,22 +7,22 @@
package gdb_test
import (
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_Instance(t *testing.T) {
gtest.Case(t, func() {
_, err := gdb.Instance("none")
gtest.AssertNE(err, nil)
gtest.Case(t, func() {
_, err := gdb.Instance("none")
gtest.AssertNE(err, nil)
db, err := gdb.Instance()
gtest.Assert(err, nil)
db, err := gdb.Instance()
gtest.Assert(err, nil)
err1 := db.PingMaster()
err2 := db.PingSlave()
gtest.Assert(err1, nil)
gtest.Assert(err2, nil)
})
err1 := db.PingMaster()
err2 := db.PingSlave()
gtest.Assert(err1, nil)
gtest.Assert(err2, nil)
})
}

View File

@ -7,18 +7,18 @@
package gdb_test
import (
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"fmt"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/database/gdb"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"os"
)
const (
// 初始化表数据量
INIT_DATA_SIZE = 10
// 初始化表数据量
INIT_DATA_SIZE = 10
)
var (
@ -45,35 +45,35 @@ func init() {
if hostname == "ijohn" {
node.Pass = "12345678"
}
gdb.AddConfigNode("test", node)
gdb.AddConfigNode("test", node)
gdb.AddConfigNode(gdb.DEFAULT_GROUP_NAME, node)
if r, err := gdb.New(); err != nil {
gtest.Fatal(err)
gtest.Fatal(err)
} else {
db = r
}
// 准备测试数据结构
if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS `test` CHARACTER SET UTF8"); err != nil {
gtest.Fatal(err)
}
// 选择操作数据库
db.SetSchema("test")
// 创建默认用户表
createTable("user")
if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS `test` CHARACTER SET UTF8"); err != nil {
gtest.Fatal(err)
}
// 选择操作数据库
db.SetSchema("test")
// 创建默认用户表
createTable("user")
}
// 创建指定名称的user测试表当table为空时创建随机的表名。
// 创建的测试表默认没有任何数据。
// 执行完成后返回该表名。
// TODO 支持更多数据库
func createTable(table...string) (name string) {
if len(table) > 0 {
name = table[0]
} else {
name = fmt.Sprintf(`user_%d`, gtime.Nanosecond())
}
dropTable(name)
if _, err := db.Exec(fmt.Sprintf(`
func createTable(table ...string) (name string) {
if len(table) > 0 {
name = table[0]
} else {
name = fmt.Sprintf(`user_%d`, gtime.Nanosecond())
}
dropTable(name)
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE %s (
id int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
passport varchar(45) NOT NULL COMMENT '账号',
@ -83,37 +83,37 @@ func createTable(table...string) (name string) {
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, name)); err != nil {
gtest.Fatal(err)
}
return
gtest.Fatal(err)
}
return
}
// 删除指定表.
func dropTable(table string) {
if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
gtest.Fatal(err)
}
if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
gtest.Fatal(err)
}
}
// See createTable.
// 创建测试表,并初始化默认数据。
func createInitTable(table...string) (name string) {
name = createTable(table...)
array := garray.New(true)
for i := 1; i <= INIT_DATA_SIZE; i++ {
array.Append(g.Map{
"id" : i,
"passport" : fmt.Sprintf(`t%d`, i),
"password" : fmt.Sprintf(`p%d`, i),
"nickname" : fmt.Sprintf(`T%d`, i),
"create_time" : gtime.Now().String(),
})
}
result, err := db.Table(name).Data(array.Slice()).Insert()
gtest.Assert(err, nil)
func createInitTable(table ...string) (name string) {
name = createTable(table...)
array := garray.New(true)
for i := 1; i <= INIT_DATA_SIZE; i++ {
array.Append(g.Map{
"id": i,
"passport": fmt.Sprintf(`t%d`, i),
"password": fmt.Sprintf(`p%d`, i),
"nickname": fmt.Sprintf(`T%d`, i),
"create_time": gtime.Now().String(),
})
}
result, err := db.Table(name).Data(array.Slice()).Insert()
gtest.Assert(err, nil)
n, e := result.RowsAffected()
gtest.Assert(e, nil)
gtest.Assert(n, INIT_DATA_SIZE)
return
n, e := result.RowsAffected()
gtest.Assert(e, nil)
gtest.Assert(n, INIT_DATA_SIZE)
return
}

View File

@ -7,29 +7,29 @@
package gdb_test
import (
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"testing"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
)
func TestDbBase_Ping(t *testing.T) {
gtest.Case(t, func() {
err1 := db.PingMaster()
err2 := db.PingSlave()
gtest.Assert(err1, nil)
gtest.Assert(err2, nil)
})
gtest.Case(t, func() {
err1 := db.PingMaster()
err2 := db.PingSlave()
gtest.Assert(err1, nil)
gtest.Assert(err2, nil)
})
}
func TestDbBase_Query(t *testing.T) {
if _, err := db.Query("SELECT ?", 1); err != nil {
gtest.Fatal(err)
}
if _, err := db.Query("ERROR"); err == nil {
gtest.Fatal("FAIL")
}
if _, err := db.Query("SELECT ?", 1); err != nil {
gtest.Fatal(err)
}
if _, err := db.Query("ERROR"); err == nil {
gtest.Fatal("FAIL")
}
}
func TestDbBase_Exec(t *testing.T) {
@ -42,461 +42,461 @@ func TestDbBase_Exec(t *testing.T) {
}
func TestDbBase_Prepare(t *testing.T) {
st, err := db.Prepare("SELECT 100")
if err != nil {
gtest.Fatal(err)
}
rows, err := st.Query()
if err != nil {
gtest.Fatal(err)
}
array, err := rows.Columns()
if err != nil {
gtest.Fatal(err)
}
gtest.Assert(array[0], "100")
if err := rows.Close(); err != nil {
gtest.Fatal(err)
}
st, err := db.Prepare("SELECT 100")
if err != nil {
gtest.Fatal(err)
}
rows, err := st.Query()
if err != nil {
gtest.Fatal(err)
}
array, err := rows.Columns()
if err != nil {
gtest.Fatal(err)
}
gtest.Assert(array[0], "100")
if err := rows.Close(); err != nil {
gtest.Fatal(err)
}
}
func TestDbBase_Insert(t *testing.T) {
if _, err := db.Insert("user", g.Map{
"id" : 1,
"passport" : "t1",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T1",
"create_time" : gtime.Now().String(),
}); err != nil {
gtest.Fatal(err)
}
// normal map
result, err := db.Insert("user", map[interface{}]interface{} {
"id" : "2",
"passport" : "t2",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T2",
"create_time" : gtime.Now().String(),
})
if err != nil {
gtest.Fatal(err)
}
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
if _, err := db.Insert("user", g.Map{
"id": 1,
"passport": "t1",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T1",
"create_time": gtime.Now().String(),
}); err != nil {
gtest.Fatal(err)
}
// normal map
result, err := db.Insert("user", map[interface{}]interface{}{
"id": "2",
"passport": "t2",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T2",
"create_time": gtime.Now().String(),
})
if err != nil {
gtest.Fatal(err)
}
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
// struct
type User struct {
Id int `gconv:"id"`
Passport string `json:"passport"`
Password string `gconv:"password"`
Nickname string `gconv:"nickname"`
CreateTime string `json:"create_time"`
}
result, err = db.Insert("user", User{
Id : 3,
Passport : "t3",
Password : "25d55ad283aa400af464c76d713c07ad",
Nickname : "T3",
CreateTime : gtime.Now().String(),
})
if err != nil {
gtest.Fatal(err)
}
n, _ = result.RowsAffected()
gtest.Assert(n, 1)
value, err := db.GetValue("select `passport` from `user` where id=?", 3)
gtest.Assert(err, nil)
gtest.Assert(value.String(), "t3")
// struct
type User struct {
Id int `gconv:"id"`
Passport string `json:"passport"`
Password string `gconv:"password"`
Nickname string `gconv:"nickname"`
CreateTime string `json:"create_time"`
}
result, err = db.Insert("user", User{
Id: 3,
Passport: "t3",
Password: "25d55ad283aa400af464c76d713c07ad",
Nickname: "T3",
CreateTime: gtime.Now().String(),
})
if err != nil {
gtest.Fatal(err)
}
n, _ = result.RowsAffected()
gtest.Assert(n, 1)
value, err := db.GetValue("select `passport` from `user` where id=?", 3)
gtest.Assert(err, nil)
gtest.Assert(value.String(), "t3")
// *struct
result, err = db.Insert("user", &User{
Id : 4,
Passport : "t4",
Password : "25d55ad283aa400af464c76d713c07ad",
Nickname : "T4",
CreateTime : gtime.Now().String(),
})
if err != nil {
gtest.Fatal(err)
}
n, _ = result.RowsAffected()
gtest.Assert(n, 1)
value, err = db.GetValue("select `passport` from `user` where id=?", 4)
gtest.Assert(err, nil)
gtest.Assert(value.String(), "t4")
// *struct
result, err = db.Insert("user", &User{
Id: 4,
Passport: "t4",
Password: "25d55ad283aa400af464c76d713c07ad",
Nickname: "T4",
CreateTime: gtime.Now().String(),
})
if err != nil {
gtest.Fatal(err)
}
n, _ = result.RowsAffected()
gtest.Assert(n, 1)
value, err = db.GetValue("select `passport` from `user` where id=?", 4)
gtest.Assert(err, nil)
gtest.Assert(value.String(), "t4")
// batch with Insert
if r, err := db.Insert("user", []interface{} {
map[interface{}]interface{} {
"id" : 200,
"passport" : "t200",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T200",
"create_time" : gtime.Now().String(),
},
map[interface{}]interface{} {
"id" : 300,
"passport" : "t300",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T300",
"create_time" : gtime.Now().String(),
},
}); err != nil {
gtest.Fatal(err)
} else {
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
}
// batch with Insert
if r, err := db.Insert("user", []interface{}{
map[interface{}]interface{}{
"id": 200,
"passport": "t200",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T200",
"create_time": gtime.Now().String(),
},
map[interface{}]interface{}{
"id": 300,
"passport": "t300",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T300",
"create_time": gtime.Now().String(),
},
}); err != nil {
gtest.Fatal(err)
} else {
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
}
// clear unnecessary data
result, err = db.Delete("user", "id>?", 1)
if err != nil {
gtest.Fatal(err)
}
n, _ = result.RowsAffected()
gtest.Assert(n, 5)
// clear unnecessary data
result, err = db.Delete("user", "id>?", 1)
if err != nil {
gtest.Fatal(err)
}
n, _ = result.RowsAffected()
gtest.Assert(n, 5)
}
func TestDbBase_BatchInsert(t *testing.T) {
gtest.Case(t, func() {
if r, err := db.BatchInsert("user", g.List {
{
"id" : 2,
"passport" : "t2",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T2",
"create_time" : gtime.Now().String(),
},
{
"id" : 3,
"passport" : "t3",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T3",
"create_time" : gtime.Now().String(),
},
}, 1); err != nil {
gtest.Fatal(err)
} else {
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
}
gtest.Case(t, func() {
if r, err := db.BatchInsert("user", g.List{
{
"id": 2,
"passport": "t2",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T2",
"create_time": gtime.Now().String(),
},
{
"id": 3,
"passport": "t3",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T3",
"create_time": gtime.Now().String(),
},
}, 1); err != nil {
gtest.Fatal(err)
} else {
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
}
result, err := db.Delete("user", "id>?", 1)
if err != nil {
gtest.Fatal(err)
}
n, _ := result.RowsAffected()
gtest.Assert(n, 2)
result, err := db.Delete("user", "id>?", 1)
if err != nil {
gtest.Fatal(err)
}
n, _ := result.RowsAffected()
gtest.Assert(n, 2)
// []interface{}
if r, err := db.BatchInsert("user", []interface{} {
map[interface{}]interface{} {
"id" : 2,
"passport" : "t2",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T2",
"create_time" : gtime.Now().String(),
},
map[interface{}]interface{} {
"id" : 3,
"passport" : "t3",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T3",
"create_time" : gtime.Now().String(),
},
}, 1); err != nil {
gtest.Fatal(err)
} else {
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
}
})
// batch insert map
gtest.Case(t, func() {
table := createTable()
defer dropTable(table)
result, err := db.BatchInsert(table, g.Map{
"id" : 1,
"passport" : "t1",
"password" : "p1",
"nickname" : "T1",
"create_time" : gtime.Now().String(),
})
if err != nil {
gtest.Fatal(err)
}
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
})
// batch insert struct
gtest.Case(t, func() {
table := createTable()
defer dropTable(table)
// []interface{}
if r, err := db.BatchInsert("user", []interface{}{
map[interface{}]interface{}{
"id": 2,
"passport": "t2",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T2",
"create_time": gtime.Now().String(),
},
map[interface{}]interface{}{
"id": 3,
"passport": "t3",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T3",
"create_time": gtime.Now().String(),
},
}, 1); err != nil {
gtest.Fatal(err)
} else {
n, _ := r.RowsAffected()
gtest.Assert(n, 2)
}
})
// batch insert map
gtest.Case(t, func() {
table := createTable()
defer dropTable(table)
result, err := db.BatchInsert(table, g.Map{
"id": 1,
"passport": "t1",
"password": "p1",
"nickname": "T1",
"create_time": gtime.Now().String(),
})
if err != nil {
gtest.Fatal(err)
}
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
})
// batch insert struct
gtest.Case(t, func() {
table := createTable()
defer dropTable(table)
type User struct {
Id int `gconv:"id"`
Passport string `gconv:"passport"`
Password string `gconv:"password"`
NickName string `gconv:"nickname"`
CreateTime *gtime.Time `gconv:"create_time"`
}
user := &User{
Id : 1,
Passport : "t1",
Password : "p1",
NickName : "T1",
CreateTime : gtime.Now(),
}
result, err := db.BatchInsert(table, user)
if err != nil {
gtest.Fatal(err)
}
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
})
type User struct {
Id int `gconv:"id"`
Passport string `gconv:"passport"`
Password string `gconv:"password"`
NickName string `gconv:"nickname"`
CreateTime *gtime.Time `gconv:"create_time"`
}
user := &User{
Id: 1,
Passport: "t1",
Password: "p1",
NickName: "T1",
CreateTime: gtime.Now(),
}
result, err := db.BatchInsert(table, user)
if err != nil {
gtest.Fatal(err)
}
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
})
}
func TestDbBase_Save(t *testing.T) {
if _, err := db.Save("user", g.Map{
"id" : 1,
"passport" : "t1",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T11",
"create_time" : gtime.Now().String(),
}); err != nil {
gtest.Fatal(err)
}
if _, err := db.Save("user", g.Map{
"id": 1,
"passport": "t1",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T11",
"create_time": gtime.Now().String(),
}); err != nil {
gtest.Fatal(err)
}
}
func TestDbBase_Replace(t *testing.T) {
if _, err := db.Save("user", g.Map{
"id" : 1,
"passport" : "t1",
"password" : "25d55ad283aa400af464c76d713c07ad",
"nickname" : "T111",
"create_time" : gtime.Now().String(),
}); err != nil {
gtest.Fatal(err)
}
if _, err := db.Save("user", g.Map{
"id": 1,
"passport": "t1",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T111",
"create_time": gtime.Now().String(),
}); err != nil {
gtest.Fatal(err)
}
}
func TestDbBase_Update(t *testing.T) {
if result, err := db.Update("user", "create_time='2010-10-10 00:00:01'", "id=3"); err != nil {
gtest.Fatal(err)
} else {
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
}
if result, err := db.Update("user", "create_time='2010-10-10 00:00:01'", "id=3"); err != nil {
gtest.Fatal(err)
} else {
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
}
}
func TestDbBase_GetAll(t *testing.T) {
if result, err := db.GetAll("SELECT * FROM user WHERE id=?", 1); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(len(result), 1)
}
if result, err := db.GetAll("SELECT * FROM user WHERE id=?", 1); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(len(result), 1)
}
}
func TestDbBase_GetOne(t *testing.T) {
if record, err := db.GetOne("SELECT * FROM user WHERE passport=?", "t1"); err != nil {
gtest.Fatal(err)
} else {
if record == nil {
gtest.Fatal("FAIL")
}
gtest.Assert(record["nickname"].String(), "T111")
}
if record, err := db.GetOne("SELECT * FROM user WHERE passport=?", "t1"); err != nil {
gtest.Fatal(err)
} else {
if record == nil {
gtest.Fatal("FAIL")
}
gtest.Assert(record["nickname"].String(), "T111")
}
}
func TestDbBase_GetValue(t *testing.T) {
if value, err := db.GetValue("SELECT id FROM user WHERE passport=?", "t3"); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(value.Int(), 3)
}
if value, err := db.GetValue("SELECT id FROM user WHERE passport=?", "t3"); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(value.Int(), 3)
}
}
func TestDbBase_GetCount(t *testing.T) {
if count, err := db.GetCount("SELECT * FROM user"); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(count, 3)
}
if count, err := db.GetCount("SELECT * FROM user"); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(count, 3)
}
}
func TestDbBase_GetStruct(t *testing.T) {
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime gtime.Time
}
user := new(User)
if err := db.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
}
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
user := new(User)
if err := db.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
}
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime gtime.Time
}
user := new(User)
if err := db.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
}
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
user := new(User)
if err := db.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
}
})
}
func TestDbBase_GetStructs(t *testing.T) {
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime gtime.Time
}
var users []User
if err := db.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
gtest.Fatal(err)
}
gtest.Assert(len(users), 3)
gtest.Assert(users[0].Id, 1)
gtest.Assert(users[1].Id, 2)
gtest.Assert(users[2].Id, 3)
gtest.Assert(users[0].NickName, "T111")
gtest.Assert(users[1].NickName, "T2")
gtest.Assert(users[2].NickName, "T3")
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime gtime.Time
}
var users []User
if err := db.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
gtest.Fatal(err)
}
gtest.Assert(len(users), 3)
gtest.Assert(users[0].Id, 1)
gtest.Assert(users[1].Id, 2)
gtest.Assert(users[2].Id, 3)
gtest.Assert(users[0].NickName, "T111")
gtest.Assert(users[1].NickName, "T2")
gtest.Assert(users[2].NickName, "T3")
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
var users []User
if err := db.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
gtest.Fatal(err)
}
gtest.Assert(len(users), 3)
gtest.Assert(users[0].Id, 1)
gtest.Assert(users[1].Id, 2)
gtest.Assert(users[2].Id, 3)
gtest.Assert(users[0].NickName, "T111")
gtest.Assert(users[1].NickName, "T2")
gtest.Assert(users[2].NickName, "T3")
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
var users []User
if err := db.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
gtest.Fatal(err)
}
gtest.Assert(len(users), 3)
gtest.Assert(users[0].Id, 1)
gtest.Assert(users[1].Id, 2)
gtest.Assert(users[2].Id, 3)
gtest.Assert(users[0].NickName, "T111")
gtest.Assert(users[1].NickName, "T2")
gtest.Assert(users[2].NickName, "T3")
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
})
}
func TestDbBase_GetScan(t *testing.T) {
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime gtime.Time
}
user := new(User)
if err := db.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
}
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
user := new(User)
if err := db.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
}
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime gtime.Time
}
user := new(User)
if err := db.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
}
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
user := new(User)
if err := db.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
}
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime gtime.Time
}
var users []User
if err := db.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
gtest.Fatal(err)
}
gtest.Assert(len(users), 3)
gtest.Assert(users[0].Id, 1)
gtest.Assert(users[1].Id, 2)
gtest.Assert(users[2].Id, 3)
gtest.Assert(users[0].NickName, "T111")
gtest.Assert(users[1].NickName, "T2")
gtest.Assert(users[2].NickName, "T3")
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime gtime.Time
}
var users []User
if err := db.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
gtest.Fatal(err)
}
gtest.Assert(len(users), 3)
gtest.Assert(users[0].Id, 1)
gtest.Assert(users[1].Id, 2)
gtest.Assert(users[2].Id, 3)
gtest.Assert(users[0].NickName, "T111")
gtest.Assert(users[1].NickName, "T2")
gtest.Assert(users[2].NickName, "T3")
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
var users []User
if err := db.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
gtest.Fatal(err)
}
gtest.Assert(len(users), 3)
gtest.Assert(users[0].Id, 1)
gtest.Assert(users[1].Id, 2)
gtest.Assert(users[2].Id, 3)
gtest.Assert(users[0].NickName, "T111")
gtest.Assert(users[1].NickName, "T2")
gtest.Assert(users[2].NickName, "T3")
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
})
gtest.Case(t, func() {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
var users []User
if err := db.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
gtest.Fatal(err)
}
gtest.Assert(len(users), 3)
gtest.Assert(users[0].Id, 1)
gtest.Assert(users[1].Id, 2)
gtest.Assert(users[2].Id, 3)
gtest.Assert(users[0].NickName, "T111")
gtest.Assert(users[1].NickName, "T2")
gtest.Assert(users[2].NickName, "T3")
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
})
}
func TestDbBase_Delete(t *testing.T) {
if result, err := db.Delete("user", nil); err != nil {
gtest.Fatal(err)
} else {
n, _ := result.RowsAffected()
gtest.Assert(n, 3)
}
if result, err := db.Delete("user", nil); err != nil {
gtest.Fatal(err)
} else {
n, _ := result.RowsAffected()
gtest.Assert(n, 3)
}
}
func TestDbBase_Time(t *testing.T) {
gtest.Case(t, func() {
result, err := db.Insert("user", g.Map{
"id" : 200,
"passport" : "t200",
"password" : "123456",
"nickname" : "T200",
"create_time" : time.Now(),
"id": 200,
"passport": "t200",
"password": "123456",
"nickname": "T200",
"create_time": time.Now(),
})
if err != nil {
gtest.Fatal(err)
@ -504,18 +504,18 @@ func TestDbBase_Time(t *testing.T) {
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
value, err := db.GetValue("select `passport` from `user` where id=?", 200)
gtest.Assert(err, nil)
gtest.Assert(err, nil)
gtest.Assert(value.String(), "t200")
})
gtest.Case(t, func() {
t := time.Now()
t := time.Now()
result, err := db.Insert("user", g.Map{
"id" : 300,
"passport" : "t300",
"password" : "123456",
"nickname" : "T300",
"create_time" : &t,
"id": 300,
"passport": "t300",
"password": "123456",
"nickname": "T300",
"create_time": &t,
})
if err != nil {
gtest.Fatal(err)
@ -523,7 +523,7 @@ func TestDbBase_Time(t *testing.T) {
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
value, err := db.GetValue("select `passport` from `user` where id=?", 300)
gtest.Assert(err, nil)
gtest.Assert(err, nil)
gtest.Assert(value.String(), "t300")
})
@ -534,6 +534,3 @@ func TestDbBase_Time(t *testing.T) {
gtest.Assert(n, 2)
}
}

File diff suppressed because it is too large Load Diff

View File

@ -22,25 +22,25 @@ func TestModel_Inherit_Insert(t *testing.T) {
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
}
result, err := db.Table("user").Filter().Data(User{
Passport : "john-test",
Password : "123456",
Nickname : "John",
Base : Base {
Id : 100,
Uid : 100,
CreateTime : gtime.Now().String(),
Passport: "john-test",
Password: "123456",
Nickname: "John",
Base: Base{
Id: 100,
Uid: 100,
CreateTime: gtime.Now().String(),
},
}).Insert()
gtest.Assert(err, nil)
n, _ := result.RowsAffected()
gtest.Assert(n, 1)
value, err := db.Table("user").Fields("passport").Where("id=100").Value()
gtest.Assert(err, nil)
gtest.Assert(err, nil)
gtest.Assert(value.String(), "john-test")
// Delete this test data.
_, err = db.Table("user").Where("id", 100).Delete()
@ -51,8 +51,8 @@ func TestModel_Inherit_Insert(t *testing.T) {
func TestModel_Inherit_MapToStruct(t *testing.T) {
gtest.Case(t, func() {
type Ids struct {
Id int `json:"id"`
Uid int `json:"uid"`
Id int `json:"id"`
Uid int `json:"uid"`
}
type Base struct {
Ids
@ -60,17 +60,17 @@ func TestModel_Inherit_MapToStruct(t *testing.T) {
}
type User struct {
Base
Passport string `json:"passport"`
Password string `json:"password"`
Nickname string `json:"nickname"`
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" : gtime.Now().String(),
"id": 100,
"uid": 101,
"passport": "t1",
"password": "123456",
"nickname": "T1",
"create_time": gtime.Now().String(),
}
result, err := db.Table("user").Filter().Data(data).Insert()
gtest.Assert(err, nil)
@ -78,15 +78,15 @@ func TestModel_Inherit_MapToStruct(t *testing.T) {
gtest.Assert(n, 1)
one, err := db.Table("user").Where("id=100").One()
gtest.Assert(err, nil)
gtest.Assert(err, nil)
user := new(User)
gtest.Assert(one.ToStruct(user), nil)
gtest.Assert(user.Id, data["id"])
gtest.Assert(user.Passport, data["passport"])
gtest.Assert(user.Password, data["password"])
gtest.Assert(user.Nickname, data["nickname"])
gtest.Assert(user.Id, data["id"])
gtest.Assert(user.Passport, data["passport"])
gtest.Assert(user.Password, data["password"])
gtest.Assert(user.Nickname, data["nickname"])
gtest.Assert(user.CreateTime, data["create_time"])
// Delete this test data.
@ -95,5 +95,3 @@ func TestModel_Inherit_MapToStruct(t *testing.T) {
})
}

File diff suppressed because it is too large Load Diff

View File

@ -14,23 +14,23 @@
package gredis
import (
"fmt"
"github.com/gogf/gf/g/container/gmap"
"fmt"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
"time"
"time"
)
const (
gDEFAULT_POOL_IDLE_TIMEOUT = 60 * time.Second
gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second
gDEFAULT_POOL_IDLE_TIMEOUT = 60 * time.Second
gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second
)
// Redis client.
type Redis struct {
pool *redis.Pool // Underlying connection pool.
group string // Configuration group.
config Config // Configuration.
pool *redis.Pool // Underlying connection pool.
group string // Configuration group.
config Config // Configuration.
}
// Redis connection.
@ -40,150 +40,149 @@ type Conn struct {
// Redis configuration.
type Config struct {
Host string
Port int
Db int
Pass string // Password for AUTH.
MaxIdle int // Maximum number of connections allowed to be idle (default is 0 means no idle connection)
MaxActive int // Maximum number of connections limit (default is 0 means no limit)
IdleTimeout time.Duration // Maximum idle time for connection (default is 60 seconds, not allowed to be set to 0)
MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 60 seconds, not allowed to be set to 0)
Host string
Port int
Db int
Pass string // Password for AUTH.
MaxIdle int // Maximum number of connections allowed to be idle (default is 0 means no idle connection)
MaxActive int // Maximum number of connections limit (default is 0 means no limit)
IdleTimeout time.Duration // Maximum idle time for connection (default is 60 seconds, not allowed to be set to 0)
MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 60 seconds, not allowed to be set to 0)
}
// Pool statistics.
type PoolStats struct {
redis.PoolStats
redis.PoolStats
}
var (
// Instance map
instances = gmap.NewStrAnyMap()
// Pool map.
pools = gmap.NewStrAnyMap()
// Instance map
instances = gmap.NewStrAnyMap()
// Pool map.
pools = gmap.NewStrAnyMap()
)
// New creates a redis client object with given configuration.
// Redis client maintains a connection pool automatically.
func New(config Config) *Redis {
if config.IdleTimeout == 0 {
config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT
}
if config.MaxConnLifetime == 0 {
config.MaxConnLifetime = gDEFAULT_POOL_MAX_LIFE_TIME
}
return &Redis{
config : config,
pool : pools.GetOrSetFuncLock(fmt.Sprintf("%v", config), func() interface{} {
return &redis.Pool {
IdleTimeout : config.IdleTimeout,
MaxConnLifetime : config.MaxConnLifetime,
Dial : func() (redis.Conn, error) {
c, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", config.Host, config.Port))
if err != nil {
return nil, err
}
// AUTH
if len(config.Pass) > 0 {
if _, err := c.Do("AUTH", config.Pass); err != nil {
return nil, err
}
}
// DB
if _, err := c.Do("SELECT", config.Db); err != nil {
return nil, err
}
return c, nil
},
// After the conn is taken from the connection pool, to test if the connection is available,
// If error is returned then it closes the connection object and recreate a new connection.
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
}
}).(*redis.Pool),
}
if config.IdleTimeout == 0 {
config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT
}
if config.MaxConnLifetime == 0 {
config.MaxConnLifetime = gDEFAULT_POOL_MAX_LIFE_TIME
}
return &Redis{
config: config,
pool: pools.GetOrSetFuncLock(fmt.Sprintf("%v", config), func() interface{} {
return &redis.Pool{
IdleTimeout: config.IdleTimeout,
MaxConnLifetime: config.MaxConnLifetime,
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", fmt.Sprintf("%s:%d", config.Host, config.Port))
if err != nil {
return nil, err
}
// AUTH
if len(config.Pass) > 0 {
if _, err := c.Do("AUTH", config.Pass); err != nil {
return nil, err
}
}
// DB
if _, err := c.Do("SELECT", config.Db); err != nil {
return nil, err
}
return c, nil
},
// After the conn is taken from the connection pool, to test if the connection is available,
// If error is returned then it closes the connection object and recreate a new connection.
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
}
}).(*redis.Pool),
}
}
// Instance returns an instance of redis client with specified group.
// The <group> param is unnecessary, if <group> is not passed,
// it returns a redis instance with default group.
func Instance(name ...string) *Redis {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
if config, ok := GetConfig(group); ok {
r := New(config)
r.group = group
return r
}
return nil
})
if v != nil {
return v.(*Redis)
}
return nil
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
v := instances.GetOrSetFuncLock(group, func() interface{} {
if config, ok := GetConfig(group); ok {
r := New(config)
r.group = group
return r
}
return nil
})
if v != nil {
return v.(*Redis)
}
return nil
}
// Close closes the redis connection pool,
// it will release all connections reserved by this pool.
// It is not necessary to call Close manually.
func (r *Redis) Close() error {
if r.group != "" {
// If it is an instance object, it needs to remove it from the instance Map.
instances.Remove(r.group)
}
pools.Remove(fmt.Sprintf("%v", r.config))
return r.pool.Close()
if r.group != "" {
// If it is an instance object, it needs to remove it from the instance Map.
instances.Remove(r.group)
}
pools.Remove(fmt.Sprintf("%v", r.config))
return r.pool.Close()
}
// Conn returns a raw underlying connection object,
// which expose more methods to communicate with server.
// **You should call Close function manually if you do not use this connection any further.**
func (r *Redis) Conn() *Conn {
return &Conn{ r.pool.Get() }
return &Conn{r.pool.Get()}
}
// Alias of Conn, see Conn.
func (r *Redis) GetConn() *Conn {
return r.Conn()
return r.Conn()
}
// SetMaxIdle sets the MaxIdle attribute of the connection pool.
func (r *Redis) SetMaxIdle(value int) {
r.pool.MaxIdle = value
r.pool.MaxIdle = value
}
// SetMaxActive sets the MaxActive attribute of the connection pool.
func (r *Redis) SetMaxActive(value int) {
r.pool.MaxActive = value
r.pool.MaxActive = value
}
// SetIdleTimeout sets the IdleTimeout attribute of the connection pool.
func (r *Redis) SetIdleTimeout(value time.Duration) {
r.pool.IdleTimeout = value
r.pool.IdleTimeout = value
}
// SetMaxConnLifetime sets the MaxConnLifetime attribute of the connection pool.
func (r *Redis) SetMaxConnLifetime(value time.Duration) {
r.pool.MaxConnLifetime = value
r.pool.MaxConnLifetime = value
}
// Stats returns pool's statistics.
func (r *Redis) Stats() *PoolStats {
return &PoolStats{r.pool.Stats()}
return &PoolStats{r.pool.Stats()}
}
// Do sends a command to the server and returns the received reply.
// Do automatically get a connection from pool, and close it when reply received.
// It does not really "close" the connection, but drop it back to the connection pool.
func (r *Redis) Do(command string, args ...interface{}) (interface{}, error) {
conn := &Conn{ r.pool.Get() }
defer conn.Close()
return conn.Do(command, args...)
conn := &Conn{r.pool.Get()}
defer conn.Close()
return conn.Do(command, args...)
}
// DoVar returns value from Do as gvar.Var.
@ -195,8 +194,7 @@ func (r *Redis) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
// Deprecated.
// Send writes the command to the client's output buffer.
func (r *Redis) Send(command string, args ...interface{}) error {
conn := &Conn{ r.pool.Get() }
defer conn.Close()
return conn.Send(command, args...)
conn := &Conn{r.pool.Get()}
defer conn.Close()
return conn.Send(command, args...)
}

View File

@ -9,53 +9,52 @@ package gredis
import "github.com/gogf/gf/g/container/gmap"
const (
// Default configuration group name.
DEFAULT_GROUP_NAME = "default"
// Default configuration group name.
DEFAULT_GROUP_NAME = "default"
)
var (
// Configuration groups.
configs = gmap.NewStrAnyMap()
// Configuration groups.
configs = gmap.NewStrAnyMap()
)
// SetConfig sets the global configuration for specified group.
// If <name> is not passed, it sets configuration for the default group name.
func SetConfig(config Config, name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Set(group, config)
instances.Remove(group)
func SetConfig(config Config, name ...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Set(group, config)
instances.Remove(group)
}
// GetConfig returns the global configuration with specified group name.
// If <name> is not passed, it returns configuration of the default group name.
func GetConfig(name...string) (config Config, ok bool) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
if v := configs.Get(group); v != nil {
return v.(Config), true
}
return Config{}, false
func GetConfig(name ...string) (config Config, ok bool) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
if v := configs.Get(group); v != nil {
return v.(Config), true
}
return Config{}, false
}
// RemoveConfig removes the global configuration with specified group.
// If <name> is not passed, it removes configuration of the default group name.
func RemoveConfig(name...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Remove(group)
instances.Remove(group)
func RemoveConfig(name ...string) {
group := DEFAULT_GROUP_NAME
if len(name) > 0 {
group = name[0]
}
configs.Remove(group)
instances.Remove(group)
}
// ClearConfig removes all configurations and instances of redis.
func ClearConfig() {
configs.Clear()
instances.Clear()
configs.Clear()
instances.Clear()
}

View File

@ -18,4 +18,4 @@ func (c *Conn) DoVar(command string, args ...interface{}) (*gvar.Var, error) {
func (c *Conn) ReceiveVar() (*gvar.Var, error) {
v, err := c.Receive()
return gvar.New(v, true), err
}
}

View File

@ -8,16 +8,16 @@
package gbase64
import (
"encoding/base64"
"encoding/base64"
)
// base64 encode
func Encode(str string) string {
return base64.StdEncoding.EncodeToString([]byte(str))
return base64.StdEncoding.EncodeToString([]byte(str))
}
// base64 decode
func Decode(str string) (string, error) {
s, e := base64.StdEncoding.DecodeString(str)
return string(s), e
}
s, e := base64.StdEncoding.DecodeString(str)
return string(s), e
}

Some files were not shown because too many files have changed in this diff Show More