mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
gofmt
This commit is contained in:
@ -6,4 +6,3 @@
|
||||
|
||||
// Package garray provides concurrent-safe/unsafe arrays.
|
||||
package garray
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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++
|
||||
|
||||
@ -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++
|
||||
|
||||
@ -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++
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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{}) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
|
||||
@ -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{}) {
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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"})
|
||||
}
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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 ""
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)]
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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, "")
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -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, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -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
@ -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()
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
*/
|
||||
*/
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
@ -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
@ -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...)
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
Reference in New Issue
Block a user