diff --git a/g/container/garray/garray_normal_int.go b/g/container/garray/garray_normal_int.go index 087523488..79fc49c10 100644 --- a/g/container/garray/garray_normal_int.go +++ b/g/container/garray/garray_normal_int.go @@ -29,7 +29,7 @@ func NewIntArray(unsafe...bool) *IntArray { } // NewIntArraySize create and returns an array with given size and cap. -// The param used to specify whether using array with un-concurrent-safety, +// The param 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{ @@ -39,7 +39,7 @@ func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray { } // NewIntArrayFrom creates and returns an array with given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param used to specify whether using array in un-concurrent-safety, // which is false in default. func NewIntArrayFrom(array []int, unsafe...bool) *IntArray { return &IntArray{ @@ -49,7 +49,7 @@ func NewIntArrayFrom(array []int, unsafe...bool) *IntArray { } // NewIntArrayFromCopy creates and returns an array from a copy of given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param 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)) diff --git a/g/container/garray/garray_normal_interface.go b/g/container/garray/garray_normal_interface.go index 053c4ebb1..666ad067a 100644 --- a/g/container/garray/garray_normal_interface.go +++ b/g/container/garray/garray_normal_interface.go @@ -34,7 +34,7 @@ func NewArray(unsafe...bool) *Array { } // NewArraySize create and returns an array with given size and cap. -// The param used to specify whether using array with un-concurrent-safety, +// The param 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{ @@ -54,7 +54,7 @@ func NewFromCopy(array []interface{}, unsafe...bool) *Array { } // NewArrayFrom creates and returns an array with given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param used to specify whether using array in un-concurrent-safety, // which is false in default. func NewArrayFrom(array []interface{}, unsafe...bool) *Array { return &Array{ @@ -64,7 +64,7 @@ func NewArrayFrom(array []interface{}, unsafe...bool) *Array { } // NewArrayFromCopy creates and returns an array from a copy of given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param 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)) diff --git a/g/container/garray/garray_normal_string.go b/g/container/garray/garray_normal_string.go index 8648427f1..0cd41b03c 100644 --- a/g/container/garray/garray_normal_string.go +++ b/g/container/garray/garray_normal_string.go @@ -30,7 +30,7 @@ func NewStringArray(unsafe...bool) *StringArray { } // NewStringArraySize create and returns an array with given size and cap. -// The param used to specify whether using array with un-concurrent-safety, +// The param 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{ @@ -40,7 +40,7 @@ func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray { } // NewStringArrayFrom creates and returns an array with given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param used to specify whether using array in un-concurrent-safety, // which is false in default. func NewStringArrayFrom(array []string, unsafe...bool) *StringArray { return &StringArray { @@ -50,7 +50,7 @@ func NewStringArrayFrom(array []string, unsafe...bool) *StringArray { } // NewStringArrayFromCopy creates and returns an array from a copy of given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param 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)) diff --git a/g/container/garray/garray_sorted_int.go b/g/container/garray/garray_sorted_int.go index f9ee58def..9288a0702 100644 --- a/g/container/garray/garray_sorted_int.go +++ b/g/container/garray/garray_sorted_int.go @@ -22,7 +22,7 @@ type SortedIntArray struct { mu *rwmutex.RWMutex array []int unique *gtype.Bool // Whether enable unique feature(false) - compareFunc func(v1, v2 int) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2) + 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. @@ -33,14 +33,14 @@ func NewSortedIntArray(unsafe...bool) *SortedIntArray { } // NewSortedIntArraySize create and returns an sorted array with given size and cap. -// The param used to specify whether using array with un-concurrent-safety, +// The param 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(), - compareFunc : func(v1, v2 int) int { + comparator : func(v1, v2 int) int { if v1 < v2 { return -1 } @@ -53,7 +53,7 @@ func NewSortedIntArraySize(cap int, unsafe...bool) *SortedIntArray { } // NewIntArrayFrom creates and returns an sorted array with given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param 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...) @@ -63,7 +63,7 @@ func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray { } // NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param 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)) @@ -305,7 +305,7 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) cmp := -2 for min <= max { mid = int((min + max) / 2) - cmp = a.compareFunc(value, a.array[mid]) + cmp = a.comparator(value, a.array[mid]) switch { case cmp < 0 : max = mid - 1 case cmp > 0 : min = mid + 1 @@ -336,7 +336,7 @@ func (a *SortedIntArray) Unique() *SortedIntArray { if i == len(a.array) - 1 { break } - if a.compareFunc(a.array[i], a.array[i + 1]) == 0 { + 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++ diff --git a/g/container/garray/garray_sorted_interface.go b/g/container/garray/garray_sorted_interface.go index a8ee8f8fa..c392fc468 100644 --- a/g/container/garray/garray_sorted_interface.go +++ b/g/container/garray/garray_sorted_interface.go @@ -22,45 +22,45 @@ type SortedArray struct { mu *rwmutex.RWMutex array []interface{} unique *gtype.Bool // Whether enable unique feature(false) - compareFunc func(v1, v2 interface{}) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2) + 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. -// The param used to specify whether using array with un-concurrent-safety, which is false in default. -// The param used to compare values to sort in array, +// The param used to specify whether using array in un-concurrent-safety, which is false in default. +// The param used to compare values to sort in array, // 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(compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray { - return NewSortedArraySize(0, compareFunc, 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 param used to specify whether using array with un-concurrent-safety, +// The param used to specify whether using array in un-concurrent-safety, // which is false in default. -func NewSortedArraySize(cap int, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray { +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), - compareFunc : compareFunc, + comparator : comparator, } } // NewSortedArrayFrom creates and returns an sorted array with given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param used to specify whether using array in un-concurrent-safety, // which is false in default. -func NewSortedArrayFrom(array []interface{}, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray { - a := NewSortedArraySize(0, compareFunc, unsafe...) +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.compareFunc(a.array[i], a.array[j]) < 0 + 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 . -// The param used to specify whether using array with un-concurrent-safety, +// The param used to specify whether using array in un-concurrent-safety, // which is false in default. func NewSortedArrayFromCopy(array []interface{}, unsafe...bool) *SortedArray { newArray := make([]interface{}, len(array)) @@ -77,7 +77,7 @@ func (a *SortedArray) SetArray(array []interface{}) *SortedArray { defer a.mu.Unlock() a.array = array sort.Slice(a.array, func(i, j int) bool { - return a.compareFunc(a.array[i], a.array[j]) < 0 + return a.comparator(a.array[i], a.array[j]) < 0 }) return a } @@ -89,7 +89,7 @@ func (a *SortedArray) Sort() *SortedArray { a.mu.Lock() defer a.mu.Unlock() sort.Slice(a.array, func(i, j int) bool { - return a.compareFunc(a.array[i], a.array[j]) < 0 + return a.comparator(a.array[i], a.array[j]) < 0 }) return a } @@ -306,7 +306,7 @@ func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result cmp := -2 for min <= max { mid = int((min + max) / 2) - cmp = a.compareFunc(value, a.array[mid]) + cmp = a.comparator(value, a.array[mid]) switch { case cmp < 0 : max = mid - 1 case cmp > 0 : min = mid + 1 @@ -338,7 +338,7 @@ func (a *SortedArray) Unique() *SortedArray { if i == len(a.array) - 1 { break } - if a.compareFunc(a.array[i], a.array[i + 1]) == 0 { + 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++ @@ -353,7 +353,7 @@ func (a *SortedArray) Clone() (newArray *SortedArray) { array := make([]interface{}, len(a.array)) copy(array, a.array) a.mu.RUnlock() - return NewSortedArrayFrom(array, a.compareFunc, !a.mu.IsSafe()) + return NewSortedArrayFrom(array, a.comparator, !a.mu.IsSafe()) } // Clear deletes all items of current array. diff --git a/g/container/garray/garray_sorted_string.go b/g/container/garray/garray_sorted_string.go index 4fff3d86c..28e7a2fa0 100644 --- a/g/container/garray/garray_sorted_string.go +++ b/g/container/garray/garray_sorted_string.go @@ -23,7 +23,7 @@ type SortedStringArray struct { mu *rwmutex.RWMutex array []string unique *gtype.Bool // Whether enable unique feature(false) - compareFunc func(v1, v2 string) int // Comparison function(it returns -1: v1 < v2; 0: v1 == v2; 1: v1 > v2) + 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. @@ -34,21 +34,21 @@ func NewSortedStringArray(unsafe...bool) *SortedStringArray { } // NewSortedStringArraySize create and returns an sorted array with given size and cap. -// The param used to specify whether using array with un-concurrent-safety, +// The param 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(), - compareFunc : func(v1, v2 string) int { + comparator : func(v1, v2 string) int { return strings.Compare(v1, v2) }, } } // NewSortedStringArrayFrom creates and returns an sorted array with given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param 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...) @@ -58,7 +58,7 @@ func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray } // NewSortedStringArrayFromCopy creates and returns an sorted array from a copy of given slice . -// The param used to specify whether using array with un-concurrent-safety, +// The param 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)) @@ -300,7 +300,7 @@ func (a *SortedStringArray) binSearch(value string, lock bool) (index int, resul cmp := -2 for min <= max { mid = int((min + max) / 2) - cmp = a.compareFunc(value, a.array[mid]) + cmp = a.comparator(value, a.array[mid]) switch { case cmp < 0 : max = mid - 1 case cmp > 0 : min = mid + 1 @@ -331,7 +331,7 @@ func (a *SortedStringArray) Unique() *SortedStringArray { if i == len(a.array) - 1 { break } - if a.compareFunc(a.array[i], a.array[i + 1]) == 0 { + 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++ diff --git a/g/container/glist/glist.go b/g/container/glist/glist.go index ee62ca371..868136571 100644 --- a/g/container/glist/glist.go +++ b/g/container/glist/glist.go @@ -22,7 +22,7 @@ type ( Element = list.Element ) -// 获得一个变长链表指针 +// New creates and returns a new empty doubly linked list. func New(unsafe...bool) *List { return &List { mu : rwmutex.New(unsafe...), @@ -30,7 +30,7 @@ func New(unsafe...bool) *List { } } -// 往链表头入栈数据项 +// PushFront inserts a new element with value at the front of list and returns . func (l *List) PushFront(v interface{}) (e *Element) { l.mu.Lock() e = l.list.PushFront(v) @@ -38,7 +38,7 @@ func (l *List) PushFront(v interface{}) (e *Element) { return } -// 往链表尾入栈数据项 +// PushBack inserts a new element with value at the back of list and returns . func (l *List) PushBack(v interface{}) (e *Element) { l.mu.Lock() e = l.list.PushBack(v) @@ -46,8 +46,8 @@ func (l *List) PushBack(v interface{}) (e *Element) { return } -// 批量往链表头入栈数据项 -func (l *List) BatchPushFront(values []interface{}) { +// PushFronts inserts multiple new elements with values at the front of list . +func (l *List) PushFronts(values []interface{}) { l.mu.Lock() for _, v := range values { l.list.PushFront(v) @@ -55,8 +55,8 @@ func (l *List) BatchPushFront(values []interface{}) { l.mu.Unlock() } -// 批量往链表尾入栈数据项 -func (l *List) BatchPushBack(values []interface{}) { +// PushBacks inserts multiple new elements with values at the back of list . +func (l *List) PushBacks(values []interface{}) { l.mu.Lock() for _, v := range values { l.list.PushBack(v) @@ -64,7 +64,7 @@ func (l *List) BatchPushBack(values []interface{}) { l.mu.Unlock() } -// 从链表尾端出栈数据项(删除) +// PopBack removes the element from back of and returns the value of the element. func (l *List) PopBack() (value interface{}) { l.mu.Lock() if e := l.list.Back(); e != nil { @@ -74,7 +74,7 @@ func (l *List) PopBack() (value interface{}) { return } -// 从链表头端出栈数据项(删除) +// PopFront removes the element from front of and returns the value of the element. func (l *List) PopFront() (value interface{}) { l.mu.Lock() if e := l.list.Front(); e != nil { @@ -84,8 +84,9 @@ func (l *List) PopFront() (value interface{}) { return } -// 批量从链表尾端出栈数据项(删除) -func (l *List) BatchPopBack(max int) (values []interface{}) { +// PopBacks removes elements from back of +// 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 { @@ -103,8 +104,9 @@ func (l *List) BatchPopBack(max int) (values []interface{}) { return } -// 批量从链表头端出栈数据项(删除) -func (l *List) BatchPopFront(max int) (values []interface{}) { +// PopFronts removes elements from front of +// and returns values of the removed elements as slice. +func (l *List) PopFronts(max int) (values []interface{}) { l.mu.RLock() length := l.list.Len() if length > 0 { @@ -122,17 +124,19 @@ func (l *List) BatchPopFront(max int) (values []interface{}) { return } -// 批量从链表尾端依次获取所有数据(删除) +// PopBackAll removes all elements from back of +// and returns values of the removed elements as slice. func (l *List) PopBackAll() []interface{} { - return l.BatchPopBack(-1) + return l.PopBacks(-1) } -// 批量从链表头端依次获取所有数据(删除) +// PopFrontAll removes all elements from front of +// and returns values of the removed elements as slice. func (l *List) PopFrontAll() []interface{} { - return l.BatchPopFront(-1) + return l.PopFronts(-1) } -// 从链表头获取所有数据(不删除) +// FrontAll copies and returns values of all elements from front of as slice. func (l *List) FrontAll() (values []interface{}) { l.mu.RLock() length := l.list.Len() @@ -146,7 +150,7 @@ func (l *List) FrontAll() (values []interface{}) { return } -// 从链表尾获取所有数据(不删除) +// BackAll copies and returns values of all elements from back of as slice. func (l *List) BackAll() (values []interface{}) { l.mu.RLock() length := l.list.Len() @@ -160,8 +164,8 @@ func (l *List) BackAll() (values []interface{}) { return } -// 获取链表头值(不删除) -func (l *List) FrontItem() (value interface{}) { +// FrontValue returns value of the first element of 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 @@ -170,8 +174,8 @@ func (l *List) FrontItem() (value interface{}) { return } -// 获取链表尾值(不删除) -func (l *List) BackItem() (value interface{}) { +// BackValue returns value of the last element of 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 @@ -180,7 +184,7 @@ func (l *List) BackItem() (value interface{}) { return } -// 获取表头指针 +// Front returns the first element of list or nil if the list is empty. func (l *List) Front() (e *Element) { l.mu.RLock() e = l.list.Front() @@ -188,7 +192,7 @@ func (l *List) Front() (e *Element) { return } -// 获取表位指针 +// Back returns the last element of list or nil if the list is empty. func (l *List) Back() (e *Element) { l.mu.RLock() e = l.list.Back() @@ -196,7 +200,8 @@ func (l *List) Back() (e *Element) { return } -// 获取链表长度 +// Len returns the number of elements of list . +// The complexity is O(1). func (l *List) Len() (length int) { l.mu.RLock() length = l.list.Len() @@ -204,30 +209,44 @@ func (l *List) Len() (length int) { return } +// MoveBefore moves element to its new position before

. +// If or

is not an element of , or ==

, the list is not modified. +// The element and

must not be nil. func (l *List) MoveBefore(e, p *Element) { l.mu.Lock() l.list.MoveBefore(e, p) l.mu.Unlock() } +// MoveAfter moves element to its new position after

. +// If or

is not an element of , or ==

, the list is not modified. +// The element and

must not be nil. func (l *List) MoveAfter(e, p *Element) { l.mu.Lock() l.list.MoveAfter(e, p) l.mu.Unlock() } +// MoveToFront moves element to the front of list . +// If is not an element of , 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() } +// MoveToBack moves element to the back of list . +// If is not an element of , 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() } +// PushBackList inserts a copy of an other list at the back of list . +// The lists and may be the same, but they must not be nil. func (l *List) PushBackList(other *List) { if l != other { other.mu.RLock() @@ -238,6 +257,8 @@ func (l *List) PushBackList(other *List) { l.mu.Unlock() } +// PushFrontList inserts a copy of an other list at the front of list . +// The lists and may be the same, but they must not be nil. func (l *List) PushFrontList(other *List) { if l != other { other.mu.RLock() @@ -248,7 +269,9 @@ func (l *List) PushFrontList(other *List) { l.mu.Unlock() } -// 在list中元素项p之后插入一个值为v的元素,并返回该元素,如果mark不是list中元素,则list不改变。 +// InsertAfter inserts a new element with value immediately after

and returns . +// If

is not an element of , the list is not modified. +// The

must not be nil. func (l *List) InsertAfter(v interface{}, p *Element) (e *Element) { l.mu.Lock() e = l.list.InsertAfter(v, p) @@ -256,7 +279,9 @@ func (l *List) InsertAfter(v interface{}, p *Element) (e *Element) { return } -// 在list中元素项p之前插入一个值为v的元素,并返回该元素,如果mark不是list中元素,则list不改变。 +// InsertBefore inserts a new element with value immediately before

and returns . +// If

is not an element of , the list is not modified. +// The

must not be nil. func (l *List) InsertBefore(v interface{}, p *Element) (e *Element) { l.mu.Lock() e = l.list.InsertBefore(v, p) @@ -264,7 +289,9 @@ func (l *List) InsertBefore(v interface{}, p *Element) (e *Element) { return } -// 删除数据项e, 并返回删除项的元素项 +// Remove removes from if is an element of list . +// 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) @@ -272,8 +299,8 @@ func (l *List) Remove(e *Element) (value interface{}) { return } -// 批量删除数据项 -func (l *List) BatchRemove(es []*Element) { +// Removes removes multiple elements from if are elements of list . +func (l *List) Removes(es []*Element) { l.mu.Lock() for _, e := range es { l.list.Remove(e) @@ -282,27 +309,63 @@ func (l *List) BatchRemove(es []*Element) { return } -// 删除所有数据项 +// RemoveAll removes all elements from list . func (l *List) RemoveAll() { l.mu.Lock() l.list = list.New() l.mu.Unlock() } +// See RemoveAll(). func (l *List) Clear() { l.RemoveAll() } -// 读锁操作 +// RLockFunc locks reading with given callback function within RWMutex.RLock. func (l *List) RLockFunc(f func(list *list.List)) { l.mu.RLock() defer l.mu.RUnlock() f(l.list) } -// 写锁操作 +// LockFunc locks writing with given callback function within RWMutex.Lock. func (l *List) LockFunc(f func(list *list.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) { + l.IteratorAsc(f) +} + +// IteratorAsc iterates the list in ascending order with given callback function . +// If returns true, then it continues iterating; or false to stop. +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() { + if !f(e) { + break + } + } + } + l.mu.RUnlock() +} + +// IteratorDesc iterates the list in descending order with given callback function . +// If returns true, then it continues iterating; or false to stop. +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() { + if !f(e) { + break + } + } + } + l.mu.RUnlock() } \ No newline at end of file diff --git a/g/container/gmap/gmap.go b/g/container/gmap/gmap.go index 4d2685698..7ce4b9e87 100644 --- a/g/container/gmap/gmap.go +++ b/g/container/gmap/gmap.go @@ -4,315 +4,41 @@ // If a copy of the MIT was not distributed with gm file, // You can obtain one at https://github.com/gogf/gf. -// Package gmap provides concurrent-safe/unsafe maps. +// Package gmap provides concurrent-safe/unsafe map containers. package gmap -import "github.com/gogf/gf/g/internal/rwmutex" - -type Map struct { - mu *rwmutex.RWMutex - m map[interface{}]interface{} -} +// Map based on hash table, alias of AnyAnyMap. +type Map = AnyAnyMap +type HashMap = AnyAnyMap // New returns an empty hash map. -// The param used to specify whether using map with un-concurrent-safety, +// The param used to specify whether using map in un-concurrent-safety, // which is false in default, means concurrent-safe. func New(unsafe ...bool) *Map { - return NewMap(unsafe...) + return NewAnyAnyMap(unsafe...) } -// Alias of New. See New. -func NewMap(unsafe ...bool) *Map { - return &Map{ - m : make(map[interface{}]interface{}), - mu : rwmutex.New(unsafe...), - } -} - -// NewFrom returns a hash map from given map . -// Notice that, the param map is a type of pointer, +// NewFrom returns a hash map from given map . +// Note that, the param 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 NewFrom(m map[interface{}]interface{}, unsafe...bool) *Map { - return &Map{ - m : m, - mu : rwmutex.New(unsafe...), - } +// The param used to specify whether using tree in un-concurrent-safety, +// which is false in default. +func NewFrom(data map[interface{}]interface{}, unsafe...bool) *Map { + return NewAnyAnyMapFrom(data, unsafe...) } -// NewFromArray returns a hash map from given array. -// The param given as the keys of the map, -// and as its corresponding values. -// -// If length of is greater than that of , -// the corresponding overflow map values will be the default value of its type. -func NewFromArray(keys []interface{}, values []interface{}, unsafe...bool) *Map { - m := make(map[interface{}]interface{}) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = interface{}(nil) - } - } - return &Map{ - m : m, - mu : rwmutex.New(unsafe...), - } +// NewHashMap returns an empty hash map. +// The param used to specify whether using map in un-concurrent-safety, +// which is false in default, means concurrent-safe. +func NewHashMap(unsafe ...bool) *Map { + return NewAnyAnyMap(unsafe...) } -// Iterator iterates the hash map with custom callback function . -// If f returns true, then continue iterating; or false to stop. -func (gm *Map) Iterator(f func (k interface{}, v interface{}) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } -} - -// Clone returns a new hash map with copy of current map data. -func (gm *Map) Clone(unsafe ...bool) *Map { - return NewFrom(gm.Map(), unsafe ...) -} - -// Map returns a copy of the data of the hash map. -func (gm *Map) Map() map[interface{}]interface{} { - m := make(map[interface{}]interface{}) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m -} - -// Set sets key-value to the hash map. -func (gm *Map) Set(key interface{}, val interface{}) { - gm.mu.Lock() - gm.m[key] = val - gm.mu.Unlock() -} - -// BatchSet batch sets key-values to the hash map. -func (gm *Map) BatchSet(m map[interface{}]interface{}) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() -} - -// Get returns the value by given . -func (gm *Map) Get(key interface{}) interface{} { - gm.mu.RLock() - val, _ := gm.m[key] - gm.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 , -// or else just return the existing value. -// -// When setting value, if is type of , -// it will be executed with mutex.Lock of the hash map, -// and its return value will be set to the map with . -// -// It returns value with given . -func (gm *Map) doSetWithLockCheck(key interface{}, value interface{}) interface{} { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - if f, ok := value.(func() interface {}); ok { - value = f() - } - gm.m[key] = value - return value -} - -// GetOrSet returns the value by key, -// or set value with given if not exist and returns this value. -func (gm *Map) GetOrSet(key interface{}, value interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } -} - -// GetOrSetFunc returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -func (gm *Map) GetOrSetFunc(key interface{}, f func() interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } -} - -// GetOrSetFuncLock returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -// -// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function -// with mutex.Lock of the hash map. -func (gm *Map) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, f) - } else { - return v - } -} - -// SetIfNotExist sets to the map if the does not exist, then return true. -// It returns false if exists, and would be ignored. -func (gm *Map) SetIfNotExist(key interface{}, value interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false -} - -// SetIfNotExistFunc sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -func (gm *Map) SetIfNotExistFunc(key interface{}, f func() interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f()) - return true - } - return false -} - -// SetIfNotExistFuncLock sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -// -// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that -// it executes function with mutex.Lock of the hash map. -func (gm *Map) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f) - return true - } - return false -} - -// BatchRemove batch deletes values of the map by keys. -func (gm *Map) BatchRemove(keys []interface{}) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() -} - -// Remove deletes value from map by given , and return this deleted value. -func (gm *Map) Remove(key interface{}) interface{} { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val -} - -// Keys returns all keys of the map as a slice. -func (gm *Map) Keys() []interface{} { - gm.mu.RLock() - keys := make([]interface{}, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys -} - -// Values returns all values of the map as a slice. -func (gm *Map) Values() []interface{} { - gm.mu.RLock() - vals := make([]interface{}, 0) - for _, val := range gm.m { - vals = append(vals, val) - } - gm.mu.RUnlock() - return vals -} - -// Contains checks whether a key exists. -// It returns true if the exists, or else false. -func (gm *Map) Contains(key interface{}) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists -} - -// Size returns the size of the map. -func (gm *Map) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length -} - -// IsEmpty checks whether the map is empty. -// It returns true if map is empty, or else false. -func (gm *Map) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty -} - -// Clear deletes all data of the map, it will remake a new underlying map data map. -func (gm *Map) Clear() { - gm.mu.Lock() - gm.m = make(map[interface{}]interface{}) - gm.mu.Unlock() -} - -// LockFunc locks writing with given callback function and mutex.Lock. -func (gm *Map) LockFunc(f func(m map[interface{}]interface{})) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) -} - -// RLockFunc locks reading with given callback function and mutex.RLock. -func (gm *Map) RLockFunc(f func(m map[interface{}]interface{})) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) -} - -// Flip exchanges key-value of the map, it will change key-value to value-key. -func (gm *Map) Flip() { - gm.mu.Lock() - defer gm.mu.Unlock() - n := make(map[interface{}]interface{}, len(gm.m)) - for i, v := range gm.m { - n[v] = i - } - gm.m = n -} - -// Merge merges two hash maps. -// The map will be merged into the map . -func (gm *Map) Merge(other *Map) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } +// NewHashMapFrom returns a hash map from given map . +// Note that, the param map will be set as the underlying data map(no deep copy), +// there might be some concurrent-safe issues when changing the map outside. +// The param used to specify whether using tree in un-concurrent-safety, +// which is false in default. +func NewHashMapFrom(data map[interface{}]interface{}, unsafe...bool) *Map { + return NewAnyAnyMapFrom(data, unsafe...) } \ No newline at end of file diff --git a/g/container/gmap/gmap_hash_any_any_map.go b/g/container/gmap/gmap_hash_any_any_map.go new file mode 100644 index 000000000..db7d217c0 --- /dev/null +++ b/g/container/gmap/gmap_hash_any_any_map.go @@ -0,0 +1,330 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gmap + +import ( + "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/internal/rwmutex" +) + +type AnyAnyMap struct { + mu *rwmutex.RWMutex + data map[interface{}]interface{} +} + +// NewAnyAnyMap returns an empty hash map. +// The param used to specify whether using map in un-concurrent-safety, +// which is false in default, means concurrent-safe. +func NewAnyAnyMap(unsafe ...bool) *AnyAnyMap { + return &AnyAnyMap{ + mu : rwmutex.New(unsafe...), + data : make(map[interface{}]interface{}), + } +} + +// NewAnyAnyMapFrom returns a hash map from given map . +// Note that, the param 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, + } +} + +// Iterator iterates the hash map with custom callback function . +// If 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 + } + } +} + +// Clone returns a new hash map with copy of current map data. +func (m *AnyAnyMap) Clone(unsafe ...bool) *AnyAnyMap { + 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() + data := make(map[interface{}]interface{}, len(m.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() +} + +// 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() +} + +// Search searches the map with given . +// Second return parameter is true if key was found, otherwise false. +func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) { + m.mu.RLock() + value, found = m.data[key] + m.mu.RUnlock() + return +} + +// Get returns the value by given . +func (m *AnyAnyMap) Get(key interface{}) 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 , +// or else just return the existing value. +// +// When setting value, if is type of , +// it will be executed with mutex.Lock of the hash map, +// and its return value will be set to the map with . +// +// It returns value with given . +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 +} + +// GetOrSet returns the value by key, +// or set value with given 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 + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function if not exist +// 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 + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// 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 + } +} + +// GetVar returns a gvar.Var with the value by given . +// The returned gvar.Var is un-concurrent safe. +func (m *AnyAnyMap) GetVar(key interface{}) *gvar.Var { + return gvar.New(m.Get(key), true) +} + +// GetVarOrSet returns a gvar.Var with result from GetVarOrSet. +// The returned gvar.Var is un-concurrent safe. +func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var { + return gvar.New(m.GetOrSet(key, value), true) +} + +// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc. +// The returned gvar.Var is un-concurrent safe. +func (m *AnyAnyMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(m.GetOrSetFunc(key, f), true) +} + +// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock. +// The returned gvar.Var is un-concurrent safe. +func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(m.GetOrSetFuncLock(key, f), true) +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (m *AnyAnyMap) SetIfNotExist(key interface{}, value interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (m *AnyAnyMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f) + return true + } + return false +} + +// Remove deletes value from map by given , 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 +} + +// Removes batch deletes values of the map by keys. +func (m *AnyAnyMap) Removes(keys []interface{}) { + m.mu.Lock() + for _, key := range keys { + delete(m.data, key) + } + m.mu.Unlock() +} + +// 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 +} + +// 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 +} + +// Contains checks whether a key exists. +// It returns true if the exists, or else false. +func (m *AnyAnyMap) Contains(key interface{}) bool { + 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 +} + +// 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 +} + +// 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() +} + +// LockFunc locks writing with given callback function within RWMutex.Lock. +func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) { + m.mu.Lock() + defer m.mu.Unlock() + f(m.data) +} + +// RLockFunc locks reading with given callback function within RWMutex.RLock. +func (m *AnyAnyMap) RLockFunc(f func(m map[interface{}]interface{})) { + 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 +} + +// Merge merges two hash maps. +// The map will be merged into the map . +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 + } +} \ No newline at end of file diff --git a/g/container/gmap/gmap_hash_int_any_map.go b/g/container/gmap/gmap_hash_int_any_map.go new file mode 100644 index 000000000..1be1c9229 --- /dev/null +++ b/g/container/gmap/gmap_hash_int_any_map.go @@ -0,0 +1,334 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. +// + +package gmap + +import ( + "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/util/gconv" +) + +type IntAnyMap struct { + mu *rwmutex.RWMutex + data map[int]interface{} +} + +// NewIntAnyMap returns an empty IntAnyMap object. +// The param used to specify whether using map in un-concurrent-safety, +// which is false in default, means concurrent-safe. +func NewIntAnyMap(unsafe...bool) *IntAnyMap { + return &IntAnyMap{ + mu : rwmutex.New(unsafe...), + data : make(map[int]interface{}), + } +} + +// NewIntAnyMapFrom returns a hash map from given map . +// Note that, the param 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, + } +} + +// Iterator iterates the hash map with custom callback function . +// If 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 + } + } +} + +// Clone returns a new hash map with copy of current map data. +func (m *IntAnyMap) Clone() *IntAnyMap { + return NewIntAnyMapFrom(m.Map(), !m.mu.IsSafe()) +} + +// Map returns a copy of the data of the hash map. +func (m *IntAnyMap) Map() map[int]interface{} { + m.mu.RLock() + data := make(map[int]interface{}, len(m.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 *IntAnyMap) Set(key int, val interface{}) { + m.mu.Lock() + m.data[key] = val + m.mu.Unlock() +} + +// Sets batch sets key-values to the hash map. +func (m *IntAnyMap) Sets(data map[int]interface{}) { + m.mu.Lock() + for k, v := range data { + m.data[k] = v + } + m.mu.Unlock() +} + +// Search searches the map with given . +// Second return parameter is true if key was found, otherwise false. +func (m *IntAnyMap) Search(key int) (value interface{}, found bool) { + m.mu.RLock() + value, found = m.data[key] + m.mu.RUnlock() + return +} + +// Get returns the value by given . +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 , +// or else just return the existing value. +// +// When setting value, if is type of , +// it will be executed with mutex.Lock of the hash map, +// and its return value will be set to the map with . +// +// It returns value with given . +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 +} + +// GetOrSet returns the value by key, +// or set value with given 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 + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function 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 + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// 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 + } +} + +// GetVar returns a gvar.Var with the value by given . +// The returned gvar.Var is un-concurrent safe. +func (m *IntAnyMap) GetVar(key int) *gvar.Var { + return gvar.New(m.Get(key), true) +} + +// GetVarOrSet returns a gvar.Var with result from GetVarOrSet. +// The returned gvar.Var is un-concurrent safe. +func (m *IntAnyMap) GetVarOrSet(key int, value interface{}) *gvar.Var { + return gvar.New(m.GetOrSet(key, value), true) +} + +// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc. +// The returned gvar.Var is un-concurrent safe. +func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() interface{}) *gvar.Var { + return gvar.New(m.GetOrSetFunc(key, f), true) +} + +// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock. +// The returned gvar.Var is un-concurrent safe. +func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var { + return gvar.New(m.GetOrSetFuncLock(key, f), true) +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (m *IntAnyMap) SetIfNotExist(key int, value interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (m *IntAnyMap) SetIfNotExistFunc(key int, f func() interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f) + return true + } + 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() +} + +// Remove deletes value from map by given , 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 +} + +// 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 +} + +// 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 +} + +// Contains checks whether a key exists. +// It returns true if the exists, or else false. +func (m *IntAnyMap) Contains(key int) bool { + 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 +} + +// 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 +} + +// 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() +} + +// LockFunc locks writing with given callback function within RWMutex.Lock. +func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) { + m.mu.Lock() + defer m.mu.Unlock() + f(m.data) +} + +// RLockFunc locks reading with given callback function within RWMutex.RLock. +func (m *IntAnyMap) RLockFunc(f func(m map[int]interface{})) { + 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 +} + +// Merge merges two hash maps. +// The map will be merged into the map . +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 + } +} \ No newline at end of file diff --git a/g/container/gmap/gmap_hash_int_int_map.go b/g/container/gmap/gmap_hash_int_int_map.go new file mode 100644 index 000000000..1be07f266 --- /dev/null +++ b/g/container/gmap/gmap_hash_int_int_map.go @@ -0,0 +1,308 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gmap + +import ( + "github.com/gogf/gf/g/internal/rwmutex" +) + +type IntIntMap struct { + mu *rwmutex.RWMutex + data map[int]int +} + +// NewIntIntMap returns an empty IntIntMap object. +// The param used to specify whether using map in un-concurrent-safety, +// which is false in default, means concurrent-safe. +func NewIntIntMap(unsafe...bool) *IntIntMap { + return &IntIntMap{ + mu : rwmutex.New(unsafe...), + data : make(map[int]int), + } +} + +// NewIntIntMapFrom returns a hash map from given map . +// Note that, the param 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, + } +} + +// Iterator iterates the hash map with custom callback function . +// If 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 + } + } +} + +// Clone returns a new hash map with copy of current map data. +func (m *IntIntMap) Clone() *IntIntMap { + return NewIntIntMapFrom(m.Map(), !m.mu.IsSafe()) +} + +// Map returns a copy of the data of the hash map. +func (m *IntIntMap) Map() map[int]int { + m.mu.RLock() + data := make(map[int]int, len(m.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 *IntIntMap) Set(key int, val int) { + m.mu.Lock() + m.data[key] = val + m.mu.Unlock() +} + +// Sets batch sets key-values to the hash map. +func (m *IntIntMap) Sets(data map[int]int) { + m.mu.Lock() + for k, v := range data { + m.data[k] = v + } + m.mu.Unlock() +} + +// Search searches the map with given . +// Second return parameter is true if key was found, otherwise false. +func (m *IntIntMap) Search(key int) (value int, found bool) { + m.mu.RLock() + value, found = m.data[key] + m.mu.RUnlock() + return +} + +// Get returns the value by given . +func (m *IntIntMap) Get(key int) (int) { + 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 , +// or else just return the existing value. +// +// It returns value with given . +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 +} + +// GetOrSet returns the value by key, +// or set value with given 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 + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function 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 + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// 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 + } +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (m *IntIntMap) SetIfNotExist(key int, value int) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (m *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool { + if !m.Contains(key) { + m.mu.Lock() + defer m.mu.Unlock() + if _, ok := m.data[key]; !ok { + m.data[key] = f() + } + return true + } + return false +} + +// 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() +} + +// Remove deletes value from map by given , 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 +} + +// 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 +} + +// 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 +} + +// Contains checks whether a key exists. +// It returns true if the exists, or else false. +func (m *IntIntMap) Contains(key int) bool { + 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 +} + +// 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 +} + +// 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() +} + +// LockFunc locks writing with given callback function within RWMutex.Lock. +func (m *IntIntMap) LockFunc(f func(m map[int]int)) { + m.mu.Lock() + defer m.mu.Unlock() + f(m.data) +} + +// RLockFunc locks reading with given callback function within RWMutex.RLock. +func (m *IntIntMap) RLockFunc(f func(m map[int]int)) { + 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 +} + +// Merge merges two hash maps. +// The map will be merged into the map . +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 + } +} diff --git a/g/container/gmap/gmap_hash_int_str_map.go b/g/container/gmap/gmap_hash_int_str_map.go new file mode 100644 index 000000000..3c985d1ac --- /dev/null +++ b/g/container/gmap/gmap_hash_int_str_map.go @@ -0,0 +1,309 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gmap + +import ( + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/util/gconv" +) + +type IntStrMap struct { + mu *rwmutex.RWMutex + data map[int]string +} + +// NewIntStrMap returns an empty IntStrMap object. +// The param used to specify whether using map in un-concurrent-safety, +// which is false in default, means concurrent-safe. +func NewIntStrMap(unsafe ...bool) *IntStrMap { + return &IntStrMap{ + mu : rwmutex.New(unsafe...), + data : make(map[int]string), + } +} + +// NewIntStrMapFrom returns a hash map from given map . +// Note that, the param 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 NewIntStrMapFrom(data map[int]string, unsafe ...bool) *IntStrMap { + return &IntStrMap{ + mu : rwmutex.New(unsafe...), + data : data, + } +} + +// Iterator iterates the hash map with custom callback function . +// If returns true, then it continues iterating; or false to stop. +func (m *IntStrMap) Iterator(f func(k int, v string) 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 *IntStrMap) Clone() *IntStrMap { + return NewIntStrMapFrom(m.Map(), !m.mu.IsSafe()) +} + +// Map returns a copy of the data of the hash map. +func (m *IntStrMap) Map() map[int]string { + m.mu.RLock() + data := make(map[int]string, len(m.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 *IntStrMap) Set(key int, val string) { + m.mu.Lock() + m.data[key] = val + m.mu.Unlock() +} + +// Sets batch sets key-values to the hash map. +func (m *IntStrMap) Sets(data map[int]string) { + m.mu.Lock() + for k, v := range data { + m.data[k] = v + } + m.mu.Unlock() +} + +// Search searches the map with given . +// Second return parameter is true if key was found, otherwise false. +func (m *IntStrMap) Search(key int) (value string, found bool) { + m.mu.RLock() + value, found = m.data[key] + m.mu.RUnlock() + return +} + +// Get returns the value by given . +func (m *IntStrMap) Get(key int) string { + 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 , +// or else just return the existing value. +// +// It returns value with given . +func (m *IntStrMap) doSetWithLockCheck(key int, value string) string { + 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 if not exist and returns this value. +func (m *IntStrMap) GetOrSet(key int, value string) string { + if v, ok := m.Search(key); !ok { + return m.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function if not exist and returns this value. +func (m *IntStrMap) GetOrSetFunc(key int, f func() string) string { + if v, ok := m.Search(key); !ok { + return m.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// with mutex.Lock of the hash map. +func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string { + 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 + } +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (m *IntStrMap) SetIfNotExist(key int, value string) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (m *IntStrMap) SetIfNotExistFunc(key int, f func() string) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool { + if !m.Contains(key) { + m.mu.Lock() + defer m.mu.Unlock() + if _, ok := m.data[key]; !ok { + m.data[key] = f() + } + return true + } + return false +} + +// Removes batch deletes values of the map by keys. +func (m *IntStrMap) Removes(keys []int) { + m.mu.Lock() + for _, key := range keys { + delete(m.data, key) + } + m.mu.Unlock() +} + +// Remove deletes value from map by given , and return this deleted value. +func (m *IntStrMap) Remove(key int) string { + 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 *IntStrMap) 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 +} + +// Values returns all values of the map as a slice. +func (m *IntStrMap) Values() []string { + m.mu.RLock() + values := make([]string, 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 exists, or else false. +func (m *IntStrMap) Contains(key int) bool { + m.mu.RLock() + _, exists := m.data[key] + m.mu.RUnlock() + return exists +} + +// Size returns the size of the map. +func (m *IntStrMap) Size() int { + 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 *IntStrMap) IsEmpty() bool { + 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 *IntStrMap) Clear() { + m.mu.Lock() + m.data = make(map[int]string) + m.mu.Unlock() +} + +// LockFunc locks writing with given callback function within RWMutex.Lock. +func (m *IntStrMap) LockFunc(f func(m map[int]string)) { + m.mu.Lock() + defer m.mu.Unlock() + f(m.data) +} + +// RLockFunc locks reading with given callback function within RWMutex.RLock. +func (m *IntStrMap) RLockFunc(f func(m map[int]string)) { + m.mu.RLock() + defer m.mu.RUnlock() + f(m.data) +} + +// Flip exchanges key-value of the map to value-key. +func (m *IntStrMap) Flip() { + m.mu.Lock() + defer m.mu.Unlock() + n := make(map[int]string, len(m.data)) + for k, v := range m.data { + n[gconv.Int(v)] = gconv.String(k) + } + m.data = n +} + +// Merge merges two hash maps. +// The map will be merged into the map . +func (m *IntStrMap) Merge(other *IntStrMap) { + 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 + } +} diff --git a/g/container/gmap/gmap_hash_str_any_map.go b/g/container/gmap/gmap_hash_str_any_map.go new file mode 100644 index 000000000..6fe1884ed --- /dev/null +++ b/g/container/gmap/gmap_hash_str_any_map.go @@ -0,0 +1,334 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. +// + +package gmap + +import ( + "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/util/gconv" +) + +type StrAnyMap struct { + mu *rwmutex.RWMutex + data map[string]interface{} +} + +// NewStrAnyMap returns an empty StrAnyMap object. +// The param used to specify whether using map in un-concurrent-safety, +// which is false in default, means concurrent-safe. +func NewStrAnyMap(unsafe ...bool) *StrAnyMap { + return &StrAnyMap{ + mu : rwmutex.New(unsafe...), + data : make(map[string]interface{}), + } +} + +// NewStrAnyMapFrom returns a hash map from given map . +// Note that, the param 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 NewStrAnyMapFrom(data map[string]interface{}, unsafe ...bool) *StrAnyMap { + return &StrAnyMap{ + mu : rwmutex.New(unsafe...), + data : data, + } +} + +// Iterator iterates the hash map with custom callback function . +// If returns true, then it continues iterating; or false to stop. +func (m *StrAnyMap) Iterator(f func(k string, 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 *StrAnyMap) Clone() *StrAnyMap { + return NewStrAnyMapFrom(m.Map(), !m.mu.IsSafe()) +} + +// Map returns a copy of the data of the hash map. +func (m *StrAnyMap) Map() map[string]interface{} { + m.mu.RLock() + data := make(map[string]interface{}, len(m.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 *StrAnyMap) Set(key string, val interface{}) { + m.mu.Lock() + m.data[key] = val + m.mu.Unlock() +} + +// Sets batch sets key-values to the hash map. +func (m *StrAnyMap) Sets(data map[string]interface{}) { + m.mu.Lock() + for k, v := range data { + m.data[k] = v + } + m.mu.Unlock() +} + +// Search searches the map with given . +// Second return parameter is true if key was found, otherwise false. +func (m *StrAnyMap) Search(key string) (value interface{}, found bool) { + m.mu.RLock() + value, found = m.data[key] + m.mu.RUnlock() + return +} + +// Get returns the value by given . +func (m *StrAnyMap) Get(key string) 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 , +// or else just return the existing value. +// +// When setting value, if is type of , +// it will be executed with mutex.Lock of the hash map, +// and its return value will be set to the map with . +// +// It returns value with given . +func (m *StrAnyMap) doSetWithLockCheck(key string, 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 +} + +// GetOrSet returns the value by key, +// or set value with given if not exist and returns this value. +func (m *StrAnyMap) GetOrSet(key string, value interface{}) interface{} { + if v, ok := m.Search(key); !ok { + return m.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +func (m *StrAnyMap) GetOrSetFunc(key string, f func() interface{}) interface{} { + if v, ok := m.Search(key); !ok { + return m.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// with mutex.Lock of the hash map. +func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} { + if v, ok := m.Search(key); !ok { + return m.doSetWithLockCheck(key, f) + } else { + return v + } +} + +// GetVar returns a gvar.Var with the value by given . +// The returned gvar.Var is un-concurrent safe. +func (m *StrAnyMap) GetVar(key string) *gvar.Var { + return gvar.New(m.Get(key), true) +} + +// GetVarOrSet returns a gvar.Var with result from GetVarOrSet. +// The returned gvar.Var is un-concurrent safe. +func (m *StrAnyMap) GetVarOrSet(key string, value interface{}) *gvar.Var { + return gvar.New(m.GetOrSet(key, value), true) +} + +// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc. +// The returned gvar.Var is un-concurrent safe. +func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() interface{}) *gvar.Var { + return gvar.New(m.GetOrSetFunc(key, f), true) +} + +// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock. +// The returned gvar.Var is un-concurrent safe. +func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.Var { + return gvar.New(m.GetOrSetFuncLock(key, f), true) +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (m *StrAnyMap) SetIfNotExist(key string, value interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (m *StrAnyMap) SetIfNotExistFunc(key string, f func() interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f) + return true + } + return false +} + +// Removes batch deletes values of the map by keys. +func (m *StrAnyMap) Removes(keys []string) { + m.mu.Lock() + for _, key := range keys { + delete(m.data, key) + } + m.mu.Unlock() +} + +// Remove deletes value from map by given , and return this deleted value. +func (m *StrAnyMap) Remove(key string) interface{} { + 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 *StrAnyMap) Keys() []string { + m.mu.RLock() + keys := make([]string, 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 *StrAnyMap) 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 +} + +// Contains checks whether a key exists. +// It returns true if the exists, or else false. +func (m *StrAnyMap) Contains(key string) bool { + m.mu.RLock() + _, exists := m.data[key] + m.mu.RUnlock() + return exists +} + +// Size returns the size of the map. +func (m *StrAnyMap) Size() int { + 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 *StrAnyMap) IsEmpty() bool { + 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 *StrAnyMap) Clear() { + m.mu.Lock() + m.data = make(map[string]interface{}) + m.mu.Unlock() +} + +// LockFunc locks writing with given callback function within RWMutex.Lock. +func (m *StrAnyMap) LockFunc(f func(m map[string]interface{})) { + m.mu.Lock() + defer m.mu.Unlock() + f(m.data) +} + +// RLockFunc locks reading with given callback function within RWMutex.RLock. +func (m *StrAnyMap) RLockFunc(f func(m map[string]interface{})) { + m.mu.RLock() + defer m.mu.RUnlock() + f(m.data) +} + +// Flip exchanges key-value of the map to value-key. +func (m *StrAnyMap) Flip() { + m.mu.Lock() + defer m.mu.Unlock() + n := make(map[string]interface{}, len(m.data)) + for k, v := range m.data { + n[gconv.String(v)] = k + } + m.data = n +} + +// Merge merges two hash maps. +// The map will be merged into the map . +func (m *StrAnyMap) Merge(other *StrAnyMap) { + 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 + } +} diff --git a/g/container/gmap/gmap_hash_str_int_map.go b/g/container/gmap/gmap_hash_str_int_map.go new file mode 100644 index 000000000..d2ff6c0f3 --- /dev/null +++ b/g/container/gmap/gmap_hash_str_int_map.go @@ -0,0 +1,312 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. +// + +package gmap + +import ( + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/util/gconv" +) + +type StrIntMap struct { + mu *rwmutex.RWMutex + data map[string]int +} + +// NewStrIntMap returns an empty StrIntMap object. +// The param used to specify whether using map in un-concurrent-safety, +// which is false in default, means concurrent-safe. +func NewStrIntMap(unsafe ...bool) *StrIntMap { + return &StrIntMap{ + mu : rwmutex.New(unsafe...), + data : make(map[string]int), + } +} + +// NewStrIntMapFrom returns a hash map from given map . +// Note that, the param 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 NewStrIntMapFrom(data map[string]int, unsafe ...bool) *StrIntMap { + return &StrIntMap{ + mu : rwmutex.New(unsafe...), + data : data, + } +} + +// Iterator iterates the hash map with custom callback function . +// If returns true, then it continues iterating; or false to stop. +func (m *StrIntMap) Iterator(f func(k string, 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 *StrIntMap) Clone() *StrIntMap { + return NewStrIntMapFrom(m.Map(), !m.mu.IsSafe()) +} + +// Map returns a copy of the data of the hash map. +func (m *StrIntMap) Map() map[string]int { + m.mu.RLock() + data := make(map[string]int, len(m.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 *StrIntMap) Set(key string, val int) { + m.mu.Lock() + m.data[key] = val + m.mu.Unlock() +} + +// Sets batch sets key-values to the hash map. +func (m *StrIntMap) Sets(data map[string]int) { + m.mu.Lock() + for k, v := range data { + m.data[k] = v + } + m.mu.Unlock() +} + +// Search searches the map with given . +// Second return parameter is true if key was found, otherwise false. +func (m *StrIntMap) Search(key string) (value int, found bool) { + m.mu.RLock() + value, found = m.data[key] + m.mu.RUnlock() + return +} + +// Get returns the value by given . +func (m *StrIntMap) Get(key string) int { + 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 , +// or else just return the existing value. +// +// It returns value with given . +func (m *StrIntMap) doSetWithLockCheck(key string, 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 +} + +// GetOrSet returns the value by key, +// or set value with given if not exist and returns this value. +func (m *StrIntMap) GetOrSet(key string, value int) int { + if v, ok := m.Search(key); !ok { + return m.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +func (m *StrIntMap) GetOrSetFunc(key string, f func() int) int { + if v, ok := m.Search(key); !ok { + return m.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// with mutex.Lock of the hash map. +func (m *StrIntMap) GetOrSetFuncLock(key string, 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 + } +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (m *StrIntMap) SetIfNotExist(key string, value int) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (m *StrIntMap) SetIfNotExistFunc(key string, f func() int) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool { + if !m.Contains(key) { + m.mu.Lock() + defer m.mu.Unlock() + if _, ok := m.data[key]; !ok { + m.data[key] = f() + } + return true + } + return false +} + +// Removes batch deletes values of the map by keys. +func (m *StrIntMap) Removes(keys []string) { + m.mu.Lock() + for _, key := range keys { + delete(m.data, key) + } + m.mu.Unlock() +} + +// Remove deletes value from map by given , and return this deleted value. +func (m *StrIntMap) Remove(key string) int { + 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 *StrIntMap) Keys() []string { + m.mu.RLock() + keys := make([]string, 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 *StrIntMap) 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 +} + +// Contains checks whether a key exists. +// It returns true if the exists, or else false. +func (m *StrIntMap) Contains(key string) bool { + m.mu.RLock() + _, exists := m.data[key] + m.mu.RUnlock() + return exists +} + +// Size returns the size of the map. +func (m *StrIntMap) Size() int { + 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 *StrIntMap) IsEmpty() bool { + 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 *StrIntMap) Clear() { + m.mu.Lock() + m.data = make(map[string]int) + m.mu.Unlock() +} + +// LockFunc locks writing with given callback function within RWMutex.Lock. +func (m *StrIntMap) LockFunc(f func(m map[string]int)) { + m.mu.Lock() + defer m.mu.Unlock() + f(m.data) +} + +// RLockFunc locks reading with given callback function within RWMutex.RLock. +func (m *StrIntMap) RLockFunc(f func(m map[string]int)) { + m.mu.RLock() + defer m.mu.RUnlock() + f(m.data) +} + +// Flip exchanges key-value of the map to value-key. +func (m *StrIntMap) Flip() { + m.mu.Lock() + defer m.mu.Unlock() + n := make(map[string]int, len(m.data)) + for k, v := range m.data { + n[gconv.String(v)] = gconv.Int(k) + } + m.data = n +} + +// Merge merges two hash maps. +// The map will be merged into the map . +func (m *StrIntMap) Merge(other *StrIntMap) { + 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 + } +} diff --git a/g/container/gmap/gmap_hash_str_str_map.go b/g/container/gmap/gmap_hash_str_str_map.go new file mode 100644 index 000000000..143f103fb --- /dev/null +++ b/g/container/gmap/gmap_hash_str_str_map.go @@ -0,0 +1,311 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. +// + +package gmap + +import ( + "github.com/gogf/gf/g/internal/rwmutex" +) + +type StrStrMap struct { + mu *rwmutex.RWMutex + data map[string]string +} + +// NewStrStrMap returns an empty StrStrMap object. +// The param used to specify whether using map in un-concurrent-safety, +// which is false in default, means concurrent-safe. +func NewStrStrMap(unsafe...bool) *StrStrMap { + return &StrStrMap{ + data : make(map[string]string), + mu : rwmutex.New(unsafe...), + } +} + +// NewStrStrMapFrom returns a hash map from given map . +// Note that, the param 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, + } +} + +// Iterator iterates the hash map with custom callback function . +// If returns true, then it continues iterating; or false to stop. +func (m *StrStrMap) Iterator(f func (k string, v string) 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 *StrStrMap) Clone() *StrStrMap { + 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() + data := make(map[string]string, len(m.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 *StrStrMap) Set(key string, val string) { + m.mu.Lock() + m.data[key] = val + m.mu.Unlock() +} + +// 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() +} + +// Search searches the map with given . +// Second return parameter is true if key was found, otherwise false. +func (m *StrStrMap) Search(key string) (value string, found bool) { + m.mu.RLock() + value, found = m.data[key] + m.mu.RUnlock() + return +} + +// Get returns the value by given . +func (m *StrStrMap) Get(key string) string { + 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 , +// or else just return the existing value. +// +// It returns value with given . +func (m *StrStrMap) doSetWithLockCheck(key string, value string) string { + 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 if not exist and returns this value. +func (m *StrStrMap) GetOrSet(key string, value string) string { + if v, ok := m.Search(key); !ok { + return m.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +func (m *StrStrMap) GetOrSetFunc(key string, f func() string) string { + if v, ok := m.Search(key); !ok { + return m.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// with mutex.Lock of the hash map. +func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string { + 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 + } +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (m *StrStrMap) SetIfNotExist(key string, value string) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (m *StrStrMap) SetIfNotExistFunc(key string, f func() string) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool { + if !m.Contains(key) { + m.mu.Lock() + defer m.mu.Unlock() + if _, ok := m.data[key]; !ok { + m.data[key] = f() + } + return true + } + return false +} + +// 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() +} + +// Remove deletes value from map by given , and return this deleted value. +func (m *StrStrMap) Remove(key string) string { + 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 *StrStrMap) Keys() []string { + m.mu.RLock() + keys := make([]string, 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 *StrStrMap) Values() []string { + m.mu.RLock() + values := make([]string, 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 exists, or else false. +func (m *StrStrMap) Contains(key string) bool { + m.mu.RLock() + _, exists := m.data[key] + m.mu.RUnlock() + return exists +} + +// Size returns the size of the map. +func (m *StrStrMap) Size() int { + 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 *StrStrMap) IsEmpty() bool { + 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 *StrStrMap) Clear() { + m.mu.Lock() + m.data = make(map[string]string) + m.mu.Unlock() +} + +// LockFunc locks writing with given callback function within RWMutex.Lock. +func (m *StrStrMap) LockFunc(f func(m map[string]string)) { + m.mu.Lock() + defer m.mu.Unlock() + f(m.data) +} + +// RLockFunc locks reading with given callback function within RWMutex.RLock. +func (m *StrStrMap) RLockFunc(f func(m map[string]string)) { + m.mu.RLock() + defer m.mu.RUnlock() + f(m.data) +} + +// Flip exchanges key-value of the map to value-key. +func (m *StrStrMap) Flip() { + m.mu.Lock() + defer m.mu.Unlock() + n := make(map[string]string, len(m.data)) + for k, v := range m.data { + n[v] = k + } + m.data = n +} + +// Merge merges two hash maps. +// The map will be merged into the map . +func (m *StrStrMap) Merge(other *StrStrMap) { + 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 + } +} diff --git a/g/container/gmap/gmap_int_bool_map.go b/g/container/gmap/gmap_int_bool_map.go deleted file mode 100644 index 519a7f05e..000000000 --- a/g/container/gmap/gmap_int_bool_map.go +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with gm file, -// You can obtain one at https://github.com/gogf/gf. -// - -package gmap - -import ( - "github.com/gogf/gf/g/internal/rwmutex" -) - -type IntBoolMap struct { - m map[int]bool - mu *rwmutex.RWMutex -} - -// NewIntBoolMap returns an empty IntBoolMap object. -// The param used to specify whether using map with un-concurrent-safety, -// which is false in default, means concurrent-safe. -func NewIntBoolMap(unsafe...bool) *IntBoolMap { - return &IntBoolMap{ - m : make(map[int]bool), - mu : rwmutex.New(unsafe...), - } -} - -// NewIntBoolMapFrom returns an IntBoolMap object from given map . -// Notice that, the param map is a type of pointer, -// there might be some concurrent-safe issues when changing the map outside. -func NewIntBoolMapFrom(m map[int]bool, unsafe...bool) *IntBoolMap { - return &IntBoolMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// NewIntBoolMapFromArray returns an IntBoolMap from given array. -// The param given as the keys of the map, -// and as its corresponding values. -// -// If length of is greater than that of , -// the corresponding overflow map values will be the default value of its type. -func NewIntBoolMapFromArray(keys []int, values []bool, unsafe...bool) *IntBoolMap { - m := make(map[int]bool) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = false - } - } - return &IntBoolMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// Clone returns a new hash map with copy of current map data. -func (gm *IntBoolMap) Clone() *IntBoolMap { - return NewIntBoolMapFrom(gm.Map(), !gm.mu.IsSafe()) -} - -// Map returns a copy of the data of the hash map. -func (gm *IntBoolMap) Map() map[int]bool { - m := make(map[int]bool) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m -} - -// Iterator iterates the hash map with custom callback function . -// If f returns true, then continue iterating; or false to stop. -func (gm *IntBoolMap) Iterator(f func (k int, v bool) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } -} - -// Set sets key-value to the hash map. -func (gm *IntBoolMap) Set(key int, val bool) { - gm.mu.Lock() - gm.m[key] = val - gm.mu.Unlock() -} - -// BatchSet batch sets key-values to the hash map. -func (gm *IntBoolMap) BatchSet(m map[int]bool) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() -} - -// Get returns the value by given . -func (gm *IntBoolMap) Get(key int) bool { - gm.mu.RLock() - val, _ := gm.m[key] - gm.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 , -// or else just return the existing value. -// -// It returns value with given . -func (gm *IntBoolMap) doSetWithLockCheck(key int, value bool) bool { - gm.mu.Lock() - if v, ok := gm.m[key]; ok { - gm.mu.Unlock() - return v - } - gm.m[key] = value - gm.mu.Unlock() - return value -} - -// GetOrSet returns the value by key, -// or set value with given if not exist and returns this value. -func (gm *IntBoolMap) GetOrSet(key int, value bool) bool { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } -} - -// GetOrSetFunc returns the value by key, -// or sets value with return value of callback function if not exist and returns this value. -func (gm *IntBoolMap) GetOrSetFunc(key int, f func() bool) bool { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } -} - -// GetOrSetFuncLock returns the value by key, -// or sets value with return value of callback function if not exist and returns this value. -// -// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function -// with mutex.Lock of the hash map. -func (gm *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool { - gm.mu.RLock() - val, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - val = f() - gm.m[key] = val - return val - } else { - return val - } -} - -// SetIfNotExist sets to the map if the does not exist, then return true. -// It returns false if exists, and would be ignored. -func (gm *IntBoolMap) SetIfNotExist(key int, value bool) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false -} - -// SetIfNotExistFunc sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -func (gm *IntBoolMap) SetIfNotExistFunc(key int, f func() bool) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f()) - return true - } - return false -} - -// SetIfNotExistFuncLock sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -// -// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that -// it executes function with mutex.Lock of the hash map. -func (gm *IntBoolMap) SetIfNotExistFuncLock(key int, f func() bool) bool { - if !gm.Contains(key) { - gm.mu.Lock() - defer gm.mu.Unlock() - if _, ok := gm.m[key]; !ok { - gm.m[key] = f() - } - return true - } - return false -} - -// BatchRemove batch deletes values of the map by keys. -func (gm *IntBoolMap) BatchRemove(keys []int) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() -} - -// Remove deletes value from map by given , and return this deleted value. -func (gm *IntBoolMap) Remove(key int) bool { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val -} - -// Keys returns all keys of the map as a slice. -func (gm *IntBoolMap) Keys() []int { - gm.mu.RLock() - keys := make([]int, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys -} - -// Contains checks whether a key exists. -// It returns true if the exists, or else false. -func (gm *IntBoolMap) Contains(key int) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists -} - -// Size returns the size of the map. -func (gm *IntBoolMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length -} - -// IsEmpty checks whether the map is empty. -// It returns true if map is empty, or else false. -func (gm *IntBoolMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty -} - -// Clear deletes all data of the map, it will remake a new underlying map data map. -func (gm *IntBoolMap) Clear() { - gm.mu.Lock() - gm.m = make(map[int]bool) - gm.mu.Unlock() -} - -// LockFunc locks writing with given callback function and mutex.Lock. -func (gm *IntBoolMap) LockFunc(f func(m map[int]bool)) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) -} - -// RLockFunc locks reading with given callback function and mutex.RLock. -func (gm *IntBoolMap) RLockFunc(f func(m map[int]bool)) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) -} - -// Merge merges two hash maps. -// The map will be merged into the map . -func (gm *IntBoolMap) Merge(other *IntBoolMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} \ No newline at end of file diff --git a/g/container/gmap/gmap_int_int_map.go b/g/container/gmap/gmap_int_int_map.go deleted file mode 100644 index 8d756479c..000000000 --- a/g/container/gmap/gmap_int_int_map.go +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with gm file, -// You can obtain one at https://github.com/gogf/gf. - - -package gmap - -import ( - "github.com/gogf/gf/g/internal/rwmutex" -) - -type IntIntMap struct { - mu *rwmutex.RWMutex - m map[int]int -} - -// NewIntIntMap returns an empty IntIntMap object. -// The param used to specify whether using map with un-concurrent-safety, -// which is false in default, means concurrent-safe. -func NewIntIntMap(unsafe...bool) *IntIntMap { - return &IntIntMap{ - m : make(map[int]int), - mu : rwmutex.New(unsafe...), - } -} - -// NewIntIntMapFrom returns an IntIntMap object from given map . -// Notice that, the param map is a type of pointer, -// there might be some concurrent-safe issues when changing the map outside. -func NewIntIntMapFrom(m map[int]int, unsafe...bool) *IntIntMap { - return &IntIntMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// NewIntIntMapFromArray returns an IntIntMap object from given array. -// The param given as the keys of the map, -// and as its corresponding values. -// -// If length of is greater than that of , -// the corresponding overflow map values will be the default value of its type. -func NewIntIntMapFromArray(keys []int, values []int, unsafe...bool) *IntIntMap { - m := make(map[int]int) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = 0 - } - } - return &IntIntMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// Iterator iterates the hash map with custom callback function . -// If f returns true, then continue iterating; or false to stop. -func (gm *IntIntMap) Iterator(f func (k int, v int) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } -} - -// Clone returns a new hash map with copy of current map data. -func (gm *IntIntMap) Clone() *IntIntMap { - return NewIntIntMapFrom(gm.Map(), !gm.mu.IsSafe()) -} - -// Map returns a copy of the data of the hash map. -func (gm *IntIntMap) Map() map[int]int { - m := make(map[int]int) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m -} - -// Set sets key-value to the hash map. -func (gm *IntIntMap) Set(key int, val int) { - gm.mu.Lock() - gm.m[key] = val - gm.mu.Unlock() -} - -// BatchSet batch sets key-values to the hash map. -func (gm *IntIntMap) BatchSet(m map[int]int) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() -} - -// Get returns the value by given . -func (gm *IntIntMap) Get(key int) (int) { - gm.mu.RLock() - val, _ := gm.m[key] - gm.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 , -// or else just return the existing value. -// -// It returns value with given . -func (gm *IntIntMap) doSetWithLockCheck(key int, value int) int { - gm.mu.Lock() - if v, ok := gm.m[key]; ok { - gm.mu.Unlock() - return v - } - gm.m[key] = value - gm.mu.Unlock() - return value -} - -// GetOrSet returns the value by key, -// or set value with given if not exist and returns this value. -func (gm *IntIntMap) GetOrSet(key int, value int) int { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } -} - -// GetOrSetFunc returns the value by key, -// or sets value with return value of callback function if not exist and returns this value. -func (gm *IntIntMap) GetOrSetFunc(key int, f func() int) int { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } -} - -// GetOrSetFuncLock returns the value by key, -// or sets value with return value of callback function if not exist and returns this value. -// -// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function -// with mutex.Lock of the hash map. -func (gm *IntIntMap) GetOrSetFuncLock(key int, f func() int) int { - gm.mu.RLock() - val, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - val = f() - gm.m[key] = val - return val - } else { - return val - } -} - -// SetIfNotExist sets to the map if the does not exist, then return true. -// It returns false if exists, and would be ignored. -func (gm *IntIntMap) SetIfNotExist(key int, value int) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false -} - -// SetIfNotExistFunc sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -func (gm *IntIntMap) SetIfNotExistFunc(key int, f func() int) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f()) - return true - } - return false -} - -// SetIfNotExistFuncLock sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -// -// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that -// it executes function with mutex.Lock of the hash map. -func (gm *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool { - if !gm.Contains(key) { - gm.mu.Lock() - defer gm.mu.Unlock() - if _, ok := gm.m[key]; !ok { - gm.m[key] = f() - } - return true - } - return false -} - -// BatchRemove batch deletes values of the map by keys. -func (gm *IntIntMap) BatchRemove(keys []int) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() -} - -// Remove deletes value from map by given , and return this deleted value. -func (gm *IntIntMap) Remove(key int) int { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val -} - -// Keys returns all keys of the map as a slice. -func (gm *IntIntMap) Keys() []int { - gm.mu.RLock() - keys := make([]int, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys -} - -// Values returns all values of the map as a slice. -func (gm *IntIntMap) Values() []int { - gm.mu.RLock() - vals := make([]int, 0) - for _, val := range gm.m { - vals = append(vals, val) - } - gm.mu.RUnlock() - return vals -} - -// Contains checks whether a key exists. -// It returns true if the exists, or else false. -func (gm *IntIntMap) Contains(key int) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists -} - -// Size returns the size of the map. -func (gm *IntIntMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length -} - -// IsEmpty checks whether the map is empty. -// It returns true if map is empty, or else false. -func (gm *IntIntMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty -} - -// Clear deletes all data of the map, it will remake a new underlying map data map. -func (gm *IntIntMap) Clear() { - gm.mu.Lock() - gm.m = make(map[int]int) - gm.mu.Unlock() -} - -// LockFunc locks writing with given callback function and mutex.Lock. -func (gm *IntIntMap) LockFunc(f func(m map[int]int)) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) -} - -// RLockFunc locks reading with given callback function and mutex.RLock. -func (gm *IntIntMap) RLockFunc(f func(m map[int]int)) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) -} - -// Flip exchanges key-value of the map, it will change key-value to value-key. -func (gm *IntIntMap) Flip() { - gm.mu.Lock() - defer gm.mu.Unlock() - n := make(map[int]int, len(gm.m)) - for k, v := range gm.m { - n[v] = k - } - gm.m = n -} - -// Merge merges two hash maps. -// The map will be merged into the map . -func (gm *IntIntMap) Merge(other *IntIntMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} diff --git a/g/container/gmap/gmap_int_interface_map.go b/g/container/gmap/gmap_int_interface_map.go deleted file mode 100644 index 54aa96ff1..000000000 --- a/g/container/gmap/gmap_int_interface_map.go +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with gm file, -// You can obtain one at https://github.com/gogf/gf. -// - -package gmap - -import ( - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/util/gconv" -) - -type IntInterfaceMap struct { - mu *rwmutex.RWMutex - m map[int]interface{} -} - -// NewIntInterfaceMap returns an empty IntInterfaceMap object. -// The param used to specify whether using map with un-concurrent-safety, -// which is false in default, means concurrent-safe. -func NewIntInterfaceMap(unsafe...bool) *IntInterfaceMap { - return &IntInterfaceMap{ - m : make(map[int]interface{}), - mu : rwmutex.New(unsafe...), - } -} - -// NewIntInterfaceMapFrom returns an IntInterfaceMap object from given map . -// Notice that, the param map is a type of pointer, -// there might be some concurrent-safe issues when changing the map outside. -func NewIntInterfaceMapFrom(m map[int]interface{}, unsafe...bool) *IntInterfaceMap { - return &IntInterfaceMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// NewFromArray returns a hash map from given array. -// The param given as the keys of the map, -// and as its corresponding values. -// -// If length of is greater than that of , -// the corresponding overflow map values will be the default value of its type. -func NewIntInterfaceMapFromArray(keys []int, values []interface{}, unsafe...bool) *IntInterfaceMap { - m := make(map[int]interface{}) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = interface{}(nil) - } - } - return &IntInterfaceMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// Iterator iterates the hash map with custom callback function . -// If f returns true, then continue iterating; or false to stop. -func (gm *IntInterfaceMap) Iterator(f func (k int, v interface{}) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } -} - -// Clone returns a new hash map with copy of current map data. -func (gm *IntInterfaceMap) Clone() *IntInterfaceMap { - return NewIntInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe()) -} - -// Map returns a copy of the data of the hash map. -func (gm *IntInterfaceMap) Map() map[int]interface{} { - m := make(map[int]interface{}) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m -} - -// Set sets key-value to the hash map. -func (gm *IntInterfaceMap) Set(key int, val interface{}) { - gm.mu.Lock() - gm.m[key] = val - gm.mu.Unlock() -} - -// BatchSet batch sets key-values to the hash map. -func (gm *IntInterfaceMap) BatchSet(m map[int]interface{}) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() -} - -// Get returns the value by given . -func (gm *IntInterfaceMap) Get(key int) (interface{}) { - gm.mu.RLock() - val, _ := gm.m[key] - gm.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 , -// or else just return the existing value. -// -// When setting value, if is type of , -// it will be executed with mutex.Lock of the hash map, -// and its return value will be set to the map with . -// -// It returns value with given . -func (gm *IntInterfaceMap) doSetWithLockCheck(key int, value interface{}) interface{} { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - if f, ok := value.(func() interface {}); ok { - value = f() - } - if value != nil { - gm.m[key] = value - } - return value -} - -// GetOrSet returns the value by key, -// or set value with given if not exist and returns this value. -func (gm *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } -} - -// GetOrSetFunc returns the value by key, -// or sets value with return value of callback function if not exist and returns this value. -func (gm *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } -} - -// GetOrSetFuncLock returns the value by key, -// or sets value with return value of callback function if not exist and returns this value. -// -// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function -// with mutex.Lock of the hash map. -func (gm *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, f) - } else { - return v - } -} - -// SetIfNotExist sets to the map if the does not exist, then return true. -// It returns false if exists, and would be ignored. -func (gm *IntInterfaceMap) SetIfNotExist(key int, value interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false -} - -// SetIfNotExistFunc sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -func (gm *IntInterfaceMap) SetIfNotExistFunc(key int, f func() interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f()) - return true - } - return false -} - -// SetIfNotExistFuncLock sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -// -// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that -// it executes function with mutex.Lock of the hash map. -func (gm *IntInterfaceMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f) - return true - } - return false -} - - -// BatchRemove batch deletes values of the map by keys. -func (gm *IntInterfaceMap) BatchRemove(keys []int) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() -} - -// Remove deletes value from map by given , and return this deleted value. -func (gm *IntInterfaceMap) Remove(key int) interface{} { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val -} - -// Keys returns all keys of the map as a slice. -func (gm *IntInterfaceMap) Keys() []int { - gm.mu.RLock() - keys := make([]int, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys -} - -// Values returns all values of the map as a slice. -func (gm *IntInterfaceMap) Values() []interface{} { - gm.mu.RLock() - vals := make([]interface{}, 0) - for _, val := range gm.m { - vals = append(vals, val) - } - gm.mu.RUnlock() - return vals -} - -// Contains checks whether a key exists. -// It returns true if the exists, or else false. -func (gm *IntInterfaceMap) Contains(key int) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists -} - -// Size returns the size of the map. -func (gm *IntInterfaceMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length -} - -// IsEmpty checks whether the map is empty. -// It returns true if map is empty, or else false. -func (gm *IntInterfaceMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty -} - -// Clear deletes all data of the map, it will remake a new underlying map data map. -func (gm *IntInterfaceMap) Clear() { - gm.mu.Lock() - gm.m = make(map[int]interface{}) - gm.mu.Unlock() -} - -// LockFunc locks writing with given callback function and mutex.Lock. -func (gm *IntInterfaceMap) LockFunc(f func(m map[int]interface{})) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) -} - -// RLockFunc locks reading with given callback function and mutex.RLock. -func (gm *IntInterfaceMap) RLockFunc(f func(m map[int]interface{})) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) -} - -// Flip exchanges key-value of the map, it will change key-value to value-key. -func (gm *IntInterfaceMap) Flip() { - gm.mu.Lock() - defer gm.mu.Unlock() - n := make(map[int]interface{}, len(gm.m)) - for k, v := range gm.m { - n[gconv.Int(v)] = k - } - gm.m = n -} - -// Merge merges two hash maps. -// The map will be merged into the map . -func (gm *IntInterfaceMap) Merge(other *IntInterfaceMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} \ No newline at end of file diff --git a/g/container/gmap/gmap_int_string_map.go b/g/container/gmap/gmap_int_string_map.go deleted file mode 100644 index f6290f23d..000000000 --- a/g/container/gmap/gmap_int_string_map.go +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with gm file, -// You can obtain one at https://github.com/gogf/gf. - -package gmap - -import ( - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/util/gconv" -) - -type IntStringMap struct { - mu *rwmutex.RWMutex - m map[int]string -} - -// NewIntStringMap returns an empty IntStringMap object. -// The param used to specify whether using map with un-concurrent-safety, -// which is false in default, means concurrent-safe. -func NewIntStringMap(unsafe ...bool) *IntStringMap { - return &IntStringMap{ - m: make(map[int]string), - mu: rwmutex.New(unsafe...), - } -} - -// NewIntStringMapFrom returns an IntStringMap object from given map . -// Notice that, the param map is a type of pointer, -// there might be some concurrent-safe issues when changing the map outside. -func NewIntStringMapFrom(m map[int]string, unsafe ...bool) *IntStringMap { - return &IntStringMap{ - m: m, - mu: rwmutex.New(unsafe...), - } -} - -// NewIntStringMapFromArray returns an IntStringMap object from given array. -// The param given as the keys of the map, -// and as its corresponding values. -// -// If length of is greater than that of , -// the corresponding overflow map values will be the default value of its type. -func NewIntStringMapFromArray(keys []int, values []string, unsafe ...bool) *IntStringMap { - m := make(map[int]string) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = "" - } - } - return &IntStringMap{ - m: m, - mu: rwmutex.New(unsafe...), - } -} - -// Iterator iterates the hash map with custom callback function . -// If f returns true, then continue iterating; or false to stop. -func (gm *IntStringMap) Iterator(f func(k int, v string) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } -} - -// Clone returns a new hash map with copy of current map data. -func (gm *IntStringMap) Clone() *IntStringMap { - return NewIntStringMapFrom(gm.Map(), !gm.mu.IsSafe()) -} - -// Map returns a copy of the data of the hash map. -func (gm *IntStringMap) Map() map[int]string { - m := make(map[int]string) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m -} - -// Set sets key-value to the hash map. -func (gm *IntStringMap) Set(key int, val string) { - gm.mu.Lock() - gm.m[key] = val - gm.mu.Unlock() -} - -// BatchSet batch sets key-values to the hash map. -func (gm *IntStringMap) BatchSet(m map[int]string) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() -} - -// Get returns the value by given . -func (gm *IntStringMap) Get(key int) string { - gm.mu.RLock() - val, _ := gm.m[key] - gm.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 , -// or else just return the existing value. -// -// It returns value with given . -func (gm *IntStringMap) doSetWithLockCheck(key int, value string) string { - gm.mu.Lock() - if v, ok := gm.m[key]; ok { - gm.mu.Unlock() - return v - } - gm.m[key] = value - gm.mu.Unlock() - return value -} - -// GetOrSet returns the value by key, -// or set value with given if not exist and returns this value. -func (gm *IntStringMap) GetOrSet(key int, value string) string { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } -} - -// GetOrSetFunc returns the value by key, -// or sets value with return value of callback function if not exist and returns this value. -func (gm *IntStringMap) GetOrSetFunc(key int, f func() string) string { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } -} - -// GetOrSetFuncLock returns the value by key, -// or sets value with return value of callback function if not exist and returns this value. -// -// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function -// with mutex.Lock of the hash map. -func (gm *IntStringMap) GetOrSetFuncLock(key int, f func() string) string { - gm.mu.RLock() - val, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - val = f() - gm.m[key] = val - return val - } else { - return val - } -} - -// SetIfNotExist sets to the map if the does not exist, then return true. -// It returns false if exists, and would be ignored. -func (gm *IntStringMap) SetIfNotExist(key int, value string) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false -} - -// SetIfNotExistFunc sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -func (gm *IntStringMap) SetIfNotExistFunc(key int, f func() string) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f()) - return true - } - return false -} - -// SetIfNotExistFuncLock sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -// -// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that -// it executes function with mutex.Lock of the hash map. -func (gm *IntStringMap) SetIfNotExistFuncLock(key int, f func() string) bool { - if !gm.Contains(key) { - gm.mu.Lock() - defer gm.mu.Unlock() - if _, ok := gm.m[key]; !ok { - gm.m[key] = f() - } - return true - } - return false -} - -// BatchRemove batch deletes values of the map by keys. -func (gm *IntStringMap) BatchRemove(keys []int) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() -} - -// Remove deletes value from map by given , and return this deleted value. -func (gm *IntStringMap) Remove(key int) string { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val -} - -// Keys returns all keys of the map as a slice. -func (gm *IntStringMap) Keys() []int { - gm.mu.RLock() - keys := make([]int, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys -} - -// Values returns all values of the map as a slice. -func (gm *IntStringMap) Values() []string { - gm.mu.RLock() - vals := make([]string, 0) - for _, val := range gm.m { - vals = append(vals, val) - } - gm.mu.RUnlock() - return vals -} - -// Contains checks whether a key exists. -// It returns true if the exists, or else false. -func (gm *IntStringMap) Contains(key int) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists -} - -// Size returns the size of the map. -func (gm *IntStringMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length -} - -// IsEmpty checks whether the map is empty. -// It returns true if map is empty, or else false. -func (gm *IntStringMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty -} - -// Clear deletes all data of the map, it will remake a new underlying map data map. -func (gm *IntStringMap) Clear() { - gm.mu.Lock() - gm.m = make(map[int]string) - gm.mu.Unlock() -} - -// LockFunc locks writing with given callback function and mutex.Lock. -func (gm *IntStringMap) LockFunc(f func(m map[int]string)) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) -} - -// RLockFunc locks reading with given callback function and mutex.RLock. -func (gm *IntStringMap) RLockFunc(f func(m map[int]string)) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) -} - -// Flip exchanges key-value of the map, it will change key-value to value-key. -func (gm *IntStringMap) Flip() { - gm.mu.Lock() - defer gm.mu.Unlock() - n := make(map[int]string, len(gm.m)) - for k, v := range gm.m { - n[gconv.Int(v)] = gconv.String(k) - } - gm.m = n -} - -// Merge merges two hash maps. -// The map will be merged into the map . -func (gm *IntStringMap) Merge(other *IntStringMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} diff --git a/g/container/gmap/gmap_link_map.go b/g/container/gmap/gmap_link_map.go new file mode 100644 index 000000000..886225cbd --- /dev/null +++ b/g/container/gmap/gmap_link_map.go @@ -0,0 +1,366 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gmap + +import ( + "github.com/gogf/gf/g/container/glist" + "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/internal/rwmutex" +) + +type ListMap struct { + mu *rwmutex.RWMutex + data map[interface{}]*glist.Element + list *glist.List +} + +type gListMapNode struct { + key interface{} + value interface{} +} + +// NewListMap returns an empty link map. +// ListMap is backed by a hash table to store values and doubly-linked list to store ordering. +// The param used to specify whether using map in un-concurrent-safety, +// 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), + } +} + +// NewListMapFrom returns a link map from given map . +// Note that, the param 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 +} + +// Iterator is alias of IteratorAsc. +func (m *ListMap) Iterator(f func (key, value interface{}) bool) { + m.IteratorAsc(f) +} + +// IteratorAsc iterates the map in ascending order with given callback function . +// If 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) + }) +} + +// IteratorDesc iterates the map in descending order with given callback function . +// If returns true, then it continues iterating; or false to stop. +func (m *ListMap) IteratorDesc(f func (key interface{}, value interface{}) bool) { + m.mu.RLock() + defer m.mu.RUnlock() + node := (*gListMapNode)(nil) + m.list.IteratorDesc(func(e *glist.Element) bool { + node = e.Value.(*gListMapNode) + return f(node.key, node.value) + }) +} + +// Clone returns a new link map with copy of current map data. +func (m *ListMap) Clone(unsafe ...bool) *ListMap { + return NewListMapFrom(m.Map(), unsafe ...) +} + +// Clear deletes all data of the map, it will remake a new underlying data map. +func (m *ListMap) Clear() { + m.mu.Lock() + m.data = make(map[interface{}]*glist.Element) + m.list = glist.New(true) + m.mu.Unlock() +} + +// Map returns a copy of the data of the map. +func (m *ListMap) Map() map[interface{}]interface{} { + m.mu.RLock() + node := (*gListMapNode)(nil) + data := make(map[interface{}]interface{}, len(m.data)) + m.list.IteratorAsc(func(e *glist.Element) bool { + node = e.Value.(*gListMapNode) + data[node.key] = node.value + return true + }) + 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() +} + +// 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() +} + +// Search searches the map with given . +// Second return parameter is true if key was found, otherwise false. +func (m *ListMap) Search(key interface{}) (value interface{}, found bool) { + m.mu.RLock() + if e, ok := m.data[key]; ok { + value = e.Value.(*gListMapNode).value + found = ok + } + m.mu.RUnlock() + return +} + +// Get returns the value by given . +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 +} + +// doSetWithLockCheck checks whether value of the key exists with mutex.Lock, +// if not exists, set value to the map with given , +// or else just return the existing value. +// +// When setting value, if is type of , +// it will be executed with mutex.Lock of the map, +// and its return value will be set to the map with . +// +// It returns value with given . +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 +} + +// GetOrSet returns the value by key, +// or set value with given 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 + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function if not exist +// 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 + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// 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 + } +} + +// GetVar returns a gvar.Var with the value by given . +// The returned gvar.Var is un-concurrent safe. +func (m *ListMap) GetVar(key interface{}) *gvar.Var { + return gvar.New(m.Get(key), true) +} + +// GetVarOrSet returns a gvar.Var with result from GetVarOrSet. +// The returned gvar.Var is un-concurrent safe. +func (m *ListMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var { + return gvar.New(m.GetOrSet(key, value), true) +} + +// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc. +// The returned gvar.Var is un-concurrent safe. +func (m *ListMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(m.GetOrSetFunc(key, f), true) +} + +// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock. +// The returned gvar.Var is un-concurrent safe. +func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(m.GetOrSetFuncLock(key, f), true) +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (m *ListMap) SetIfNotExist(key interface{}, value interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (m *ListMap) SetIfNotExistFunc(key interface{}, f func() interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the map. +func (m *ListMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool { + if !m.Contains(key) { + m.doSetWithLockCheck(key, f) + return true + } + return false +} + +// Remove deletes value from map by given , 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 +} + +// Removes batch deletes values of the map by keys. +func (m *ListMap) Removes(keys []interface{}) { + m.mu.Lock() + for _, key := range keys { + if e, ok := m.data[key]; ok { + delete(m.data, key) + m.list.Remove(e) + } + } + m.mu.Unlock() +} + +// 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 +} + +// 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.list.IteratorAsc(func(e *glist.Element) bool { + values[index] = e.Value.(*gListMapNode).value + index++ + return true + }) + m.mu.RUnlock() + return values +} + +// Contains checks whether a key exists. +// It returns true if the exists, or else false. +func (m *ListMap) Contains(key interface{}) (ok bool) { + 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 +} + +// 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 +} + +// 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) + } +} + +// Merge merges two link maps. +// The map will be merged into the map . +func (m *ListMap) Merge(other *ListMap) { + 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 + }) +} \ No newline at end of file diff --git a/g/container/gmap/gmap_string_bool_map.go b/g/container/gmap/gmap_string_bool_map.go deleted file mode 100644 index 6c3e14d4f..000000000 --- a/g/container/gmap/gmap_string_bool_map.go +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with gm file, -// You can obtain one at https://github.com/gogf/gf. -// - -package gmap - -import ( - "github.com/gogf/gf/g/internal/rwmutex" -) - -type StringBoolMap struct { - mu *rwmutex.RWMutex - m map[string]bool -} - -// NewStringBoolMap returns an empty StringBoolMap object. -// The param used to specify whether using map with un-concurrent-safety, -// which is false in default, means concurrent-safe. -func NewStringBoolMap(unsafe...bool) *StringBoolMap { - return &StringBoolMap{ - m : make(map[string]bool), - mu : rwmutex.New(unsafe...), - } -} - -// NewStringBoolMapFrom returns an StringBoolMap object from given map . -// Notice that, the param map is a type of pointer, -// there might be some concurrent-safe issues when changing the map outside. -func NewStringBoolMapFrom(m map[string]bool, unsafe...bool) *StringBoolMap { - return &StringBoolMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// NewFromArray returns a hash map from given array. -// The param given as the keys of the map, -// and as its corresponding values. -// -// If length of is greater than that of , -// the corresponding overflow map values will be the default value of its type. -func NewStringBoolMapFromArray(keys []string, values []bool, unsafe...bool) *StringBoolMap { - m := make(map[string]bool) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = false - } - } - return &StringBoolMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// Iterator iterates the hash map with custom callback function . -// If f returns true, then continue iterating; or false to stop. -func (gm *StringBoolMap) Iterator(f func (k string, v bool) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } -} - -// Clone returns a new hash map with copy of current map data. -func (gm *StringBoolMap) Clone() *StringBoolMap { - return NewStringBoolMapFrom(gm.Map(), !gm.mu.IsSafe()) -} - -// Map returns a copy of the data of the hash map. -func (gm *StringBoolMap) Map() map[string]bool { - m := make(map[string]bool) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m -} - -// Set sets key-value to the hash map. -func (gm *StringBoolMap) Set(key string, val bool) { - gm.mu.Lock() - gm.m[key] = val - gm.mu.Unlock() -} - -// BatchSet batch sets key-values to the hash map. -func (gm *StringBoolMap) BatchSet(m map[string]bool) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() -} - -// Get returns the value by given . -func (gm *StringBoolMap) Get(key string) bool { - gm.mu.RLock() - val, _ := gm.m[key] - gm.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 , -// or else just return the existing value. -// -// It returns value with given . -func (gm *StringBoolMap) doSetWithLockCheck(key string, value bool) bool { - gm.mu.Lock() - if v, ok := gm.m[key]; ok { - gm.mu.Unlock() - return v - } - gm.m[key] = value - gm.mu.Unlock() - return value -} - -// GetOrSet returns the value by key, -// or set value with given if not exist and returns this value. -func (gm *StringBoolMap) GetOrSet(key string, value bool) bool { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } -} - -// GetOrSetFunc returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -func (gm *StringBoolMap) GetOrSetFunc(key string, f func() bool) bool { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } -} - -// GetOrSetFuncLock returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -// -// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function -// with mutex.Lock of the hash map. -func (gm *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool { - gm.mu.RLock() - val, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - val = f() - gm.m[key] = val - return val - } else { - return val - } -} - - -// SetIfNotExist sets to the map if the does not exist, then return true. -// It returns false if exists, and would be ignored. -func (gm *StringBoolMap) SetIfNotExist(key string, value bool) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false -} - -// SetIfNotExistFunc sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -func (gm *StringBoolMap) SetIfNotExistFunc(key string, f func() bool) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f()) - return true - } - return false -} - -// SetIfNotExistFuncLock sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -// -// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that -// it executes function with mutex.Lock of the hash map. -func (gm *StringBoolMap) SetIfNotExistFuncLock(key string, f func() bool) bool { - if !gm.Contains(key) { - gm.mu.Lock() - defer gm.mu.Unlock() - if _, ok := gm.m[key]; !ok { - gm.m[key] = f() - } - return true - } - return false -} - -// BatchRemove batch deletes values of the map by keys. -func (gm *StringBoolMap) BatchRemove(keys []string) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() -} - -// Remove deletes value from map by given , and return this deleted value. -func (gm *StringBoolMap) Remove(key string) bool { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val -} - -// Keys returns all keys of the map as a slice. -func (gm *StringBoolMap) Keys() []string { - gm.mu.RLock() - keys := make([]string, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys -} - -// Contains checks whether a key exists. -// It returns true if the exists, or else false. -func (gm *StringBoolMap) Contains(key string) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists -} - -// Size returns the size of the map. -func (gm *StringBoolMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length -} - -// IsEmpty checks whether the map is empty. -// It returns true if map is empty, or else false. -func (gm *StringBoolMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty -} - -// Clear deletes all data of the map, it will remake a new underlying map data map. -func (gm *StringBoolMap) Clear() { - gm.mu.Lock() - gm.m = make(map[string]bool) - gm.mu.Unlock() -} - -// LockFunc locks writing with given callback function and mutex.Lock. -func (gm *StringBoolMap) LockFunc(f func(m map[string]bool)) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) -} - -// RLockFunc locks reading with given callback function and mutex.RLock. -func (gm *StringBoolMap) RLockFunc(f func(m map[string]bool)) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) -} - -// Merge merges two hash maps. -// The map will be merged into the map . -func (gm *StringBoolMap) Merge(other *StringBoolMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} \ No newline at end of file diff --git a/g/container/gmap/gmap_string_int_map.go b/g/container/gmap/gmap_string_int_map.go deleted file mode 100644 index 028f82c74..000000000 --- a/g/container/gmap/gmap_string_int_map.go +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with gm file, -// You can obtain one at https://github.com/gogf/gf. -// - -package gmap - -import ( - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/util/gconv" -) - -type StringIntMap struct { - mu *rwmutex.RWMutex - m map[string]int -} - -// NewStringIntMap returns an empty StringIntMap object. -// The param used to specify whether using map with un-concurrent-safety, -// which is false in default, means concurrent-safe. -func NewStringIntMap(unsafe ...bool) *StringIntMap { - return &StringIntMap{ - m: make(map[string]int), - mu: rwmutex.New(unsafe...), - } -} - -// NewStringIntMapFrom returns an StringIntMap object from given map . -// Notice that, the param map is a type of pointer, -// there might be some concurrent-safe issues when changing the map outside. -func NewStringIntMapFrom(m map[string]int, unsafe ...bool) *StringIntMap { - return &StringIntMap{ - m: m, - mu: rwmutex.New(unsafe...), - } -} - -// NewStringIntMapFromArray returns an StringIntMap object from given array. -// The param given as the keys of the map, -// and as its corresponding values. -// -// If length of is greater than that of , -// the corresponding overflow map values will be the default value of its type. -func NewStringIntMapFromArray(keys []string, values []int, unsafe ...bool) *StringIntMap { - m := make(map[string]int) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = 0 - } - } - return &StringIntMap{ - m: m, - mu: rwmutex.New(unsafe...), - } -} - -// Iterator iterates the hash map with custom callback function . -// If f returns true, then continue iterating; or false to stop. -func (gm *StringIntMap) Iterator(f func(k string, v int) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } -} - -// Clone returns a new hash map with copy of current map data. -func (gm *StringIntMap) Clone() *StringIntMap { - return NewStringIntMapFrom(gm.Map(), !gm.mu.IsSafe()) -} - -// Map returns a copy of the data of the hash map. -func (gm *StringIntMap) Map() map[string]int { - m := make(map[string]int) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m -} - -// Set sets key-value to the hash map. -func (gm *StringIntMap) Set(key string, val int) { - gm.mu.Lock() - gm.m[key] = val - gm.mu.Unlock() -} - -// BatchSet batch sets key-values to the hash map. -func (gm *StringIntMap) BatchSet(m map[string]int) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() -} - -// Get returns the value by given . -func (gm *StringIntMap) Get(key string) int { - gm.mu.RLock() - val, _ := gm.m[key] - gm.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 , -// or else just return the existing value. -// -// It returns value with given . -func (gm *StringIntMap) doSetWithLockCheck(key string, value int) int { - gm.mu.Lock() - if v, ok := gm.m[key]; ok { - gm.mu.Unlock() - return v - } - gm.m[key] = value - gm.mu.Unlock() - return value -} - -// GetOrSet returns the value by key, -// or set value with given if not exist and returns this value. -func (gm *StringIntMap) GetOrSet(key string, value int) int { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } -} - -// GetOrSetFunc returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -func (gm *StringIntMap) GetOrSetFunc(key string, f func() int) int { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } -} - -// GetOrSetFuncLock returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -// -// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function -// with mutex.Lock of the hash map. -func (gm *StringIntMap) GetOrSetFuncLock(key string, f func() int) int { - gm.mu.RLock() - val, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - val = f() - gm.m[key] = val - return val - } else { - return val - } -} - -// SetIfNotExist sets to the map if the does not exist, then return true. -// It returns false if exists, and would be ignored. -func (gm *StringIntMap) SetIfNotExist(key string, value int) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false -} - -// SetIfNotExistFunc sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -func (gm *StringIntMap) SetIfNotExistFunc(key string, f func() int) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f()) - return true - } - return false -} - -// SetIfNotExistFuncLock sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -// -// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that -// it executes function with mutex.Lock of the hash map. -func (gm *StringIntMap) SetIfNotExistFuncLock(key string, f func() int) bool { - if !gm.Contains(key) { - gm.mu.Lock() - defer gm.mu.Unlock() - if _, ok := gm.m[key]; !ok { - gm.m[key] = f() - } - return true - } - return false -} - -// BatchRemove batch deletes values of the map by keys. -func (gm *StringIntMap) BatchRemove(keys []string) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() -} - -// Remove deletes value from map by given , and return this deleted value. -func (gm *StringIntMap) Remove(key string) int { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val -} - -// Keys returns all keys of the map as a slice. -func (gm *StringIntMap) Keys() []string { - gm.mu.RLock() - keys := make([]string, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys -} - -// Values returns all values of the map as a slice. -func (gm *StringIntMap) Values() []int { - gm.mu.RLock() - vals := make([]int, 0) - for _, val := range gm.m { - vals = append(vals, val) - } - gm.mu.RUnlock() - return vals -} - -// Contains checks whether a key exists. -// It returns true if the exists, or else false. -func (gm *StringIntMap) Contains(key string) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists -} - -// Size returns the size of the map. -func (gm *StringIntMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length -} - -// IsEmpty checks whether the map is empty. -// It returns true if map is empty, or else false. -func (gm *StringIntMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty -} - -// Clear deletes all data of the map, it will remake a new underlying map data map. -func (gm *StringIntMap) Clear() { - gm.mu.Lock() - gm.m = make(map[string]int) - gm.mu.Unlock() -} - -// LockFunc locks writing with given callback function and mutex.Lock. -func (gm *StringIntMap) LockFunc(f func(m map[string]int)) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) -} - -// RLockFunc locks reading with given callback function and mutex.RLock. -func (gm *StringIntMap) RLockFunc(f func(m map[string]int)) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) -} - -// Flip exchanges key-value of the map, it will change key-value to value-key. -func (gm *StringIntMap) Flip() { - gm.mu.Lock() - defer gm.mu.Unlock() - n := make(map[string]int, len(gm.m)) - for k, v := range gm.m { - n[gconv.String(v)] = gconv.Int(k) - } - gm.m = n -} - -// Merge merges two hash maps. -// The map will be merged into the map . -func (gm *StringIntMap) Merge(other *StringIntMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} diff --git a/g/container/gmap/gmap_string_interface_map.go b/g/container/gmap/gmap_string_interface_map.go deleted file mode 100644 index 5c354ea89..000000000 --- a/g/container/gmap/gmap_string_interface_map.go +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with gm file, -// You can obtain one at https://github.com/gogf/gf. -// - -package gmap - -import ( - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/util/gconv" -) - -type StringInterfaceMap struct { - mu *rwmutex.RWMutex - m map[string]interface{} -} - -// NewStringInterfaceMap returns an empty StringInterfaceMap object. -// The param used to specify whether using map with un-concurrent-safety, -// which is false in default, means concurrent-safe. -func NewStringInterfaceMap(unsafe ...bool) *StringInterfaceMap { - return &StringInterfaceMap{ - m: make(map[string]interface{}), - mu: rwmutex.New(unsafe...), - } -} - -// NewStringInterfaceMapFrom returns an StringInterfaceMap object from given map . -// Notice that, the param map is a type of pointer, -// there might be some concurrent-safe issues when changing the map outside. -func NewStringInterfaceMapFrom(m map[string]interface{}, unsafe ...bool) *StringInterfaceMap { - return &StringInterfaceMap{ - m: m, - mu: rwmutex.New(unsafe...), - } -} - -// NewStringInterfaceMapFromArray returns an StringInterfaceMap object from given array. -// The param given as the keys of the map, -// and as its corresponding values. -// -// If length of is greater than that of , -// the corresponding overflow map values will be the default value of its type. -func NewStringInterfaceMapFromArray(keys []string, values []interface{}, unsafe ...bool) *StringInterfaceMap { - m := make(map[string]interface{}) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = interface{}(nil) - } - } - return &StringInterfaceMap{ - m: m, - mu: rwmutex.New(unsafe...), - } -} - -// Iterator iterates the hash map with custom callback function . -// If f returns true, then continue iterating; or false to stop. -func (gm *StringInterfaceMap) Iterator(f func(k string, v interface{}) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } -} - -// Clone returns a new hash map with copy of current map data. -func (gm *StringInterfaceMap) Clone() *StringInterfaceMap { - return NewStringInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe()) -} - -// Map returns a copy of the data of the hash map. -func (gm *StringInterfaceMap) Map() map[string]interface{} { - m := make(map[string]interface{}) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m -} - -// Set sets key-value to the hash map. -func (gm *StringInterfaceMap) Set(key string, val interface{}) { - gm.mu.Lock() - gm.m[key] = val - gm.mu.Unlock() -} - -// BatchSet batch sets key-values to the hash map. -func (gm *StringInterfaceMap) BatchSet(m map[string]interface{}) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() -} - -// Get returns the value by given . -func (gm *StringInterfaceMap) Get(key string) interface{} { - gm.mu.RLock() - val, _ := gm.m[key] - gm.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 , -// or else just return the existing value. -// -// When setting value, if is type of , -// it will be executed with mutex.Lock of the hash map, -// and its return value will be set to the map with . -// -// It returns value with given . -func (gm *StringInterfaceMap) doSetWithLockCheck(key string, value interface{}) interface{} { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - if f, ok := value.(func() interface{}); ok { - value = f() - } - if value != nil { - gm.m[key] = value - } - return value -} - -// GetOrSet returns the value by key, -// or set value with given if not exist and returns this value. -func (gm *StringInterfaceMap) GetOrSet(key string, value interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } -} - -// GetOrSetFunc returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -func (gm *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } -} - -// GetOrSetFuncLock returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -// -// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function -// with mutex.Lock of the hash map. -func (gm *StringInterfaceMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} { - if v := gm.Get(key); v == nil { - return gm.doSetWithLockCheck(key, f) - } else { - return v - } -} - -// SetIfNotExist sets to the map if the does not exist, then return true. -// It returns false if exists, and would be ignored. -func (gm *StringInterfaceMap) SetIfNotExist(key string, value interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false -} - -// SetIfNotExistFunc sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -func (gm *StringInterfaceMap) SetIfNotExistFunc(key string, f func() interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f()) - return true - } - return false -} - -// SetIfNotExistFuncLock sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -// -// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that -// it executes function with mutex.Lock of the hash map. -func (gm *StringInterfaceMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f) - return true - } - return false -} - -// BatchRemove batch deletes values of the map by keys. -func (gm *StringInterfaceMap) BatchRemove(keys []string) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() -} - -// Remove deletes value from map by given , and return this deleted value. -func (gm *StringInterfaceMap) Remove(key string) interface{} { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val -} - -// Keys returns all keys of the map as a slice. -func (gm *StringInterfaceMap) Keys() []string { - gm.mu.RLock() - keys := make([]string, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys -} - -// Values returns all values of the map as a slice. -func (gm *StringInterfaceMap) Values() []interface{} { - gm.mu.RLock() - vals := make([]interface{}, 0) - for _, val := range gm.m { - vals = append(vals, val) - } - gm.mu.RUnlock() - return vals -} - -// Contains checks whether a key exists. -// It returns true if the exists, or else false. -func (gm *StringInterfaceMap) Contains(key string) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists -} - -// Size returns the size of the map. -func (gm *StringInterfaceMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length -} - -// IsEmpty checks whether the map is empty. -// It returns true if map is empty, or else false. -func (gm *StringInterfaceMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty -} - -// Clear deletes all data of the map, it will remake a new underlying map data map. -func (gm *StringInterfaceMap) Clear() { - gm.mu.Lock() - gm.m = make(map[string]interface{}) - gm.mu.Unlock() -} - -// LockFunc locks writing with given callback function and mutex.Lock. -func (gm *StringInterfaceMap) LockFunc(f func(m map[string]interface{})) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) -} - -// RLockFunc locks reading with given callback function and mutex.RLock. -func (gm *StringInterfaceMap) RLockFunc(f func(m map[string]interface{})) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) -} - -// Flip exchanges key-value of the map, it will change key-value to value-key. -func (gm *StringInterfaceMap) Flip() { - gm.mu.Lock() - defer gm.mu.Unlock() - n := make(map[string]interface{}, len(gm.m)) - for k, v := range gm.m { - n[gconv.String(v)] = k - } - gm.m = n -} - -// Merge merges two hash maps. -// The map will be merged into the map . -func (gm *StringInterfaceMap) Merge(other *StringInterfaceMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} diff --git a/g/container/gmap/gmap_string_string_map.go b/g/container/gmap/gmap_string_string_map.go deleted file mode 100644 index 8eb906853..000000000 --- a/g/container/gmap/gmap_string_string_map.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with gm file, -// You can obtain one at https://github.com/gogf/gf. -// - -package gmap - -import ( - "github.com/gogf/gf/g/internal/rwmutex" -) - -type StringStringMap struct { - mu *rwmutex.RWMutex - m map[string]string -} - -// NewStringStringMap returns an empty StringStringMap object. -// The param used to specify whether using map with un-concurrent-safety, -// which is false in default, means concurrent-safe. -func NewStringStringMap(unsafe...bool) *StringStringMap { - return &StringStringMap{ - m : make(map[string]string), - mu : rwmutex.New(unsafe...), - } -} - -// NewStringStringMapFrom returns an StringStringMap object from given map . -// Notice that, the param map is a type of pointer, -// there might be some concurrent-safe issues when changing the map outside. -func NewStringStringMapFrom(m map[string]string, unsafe...bool) *StringStringMap { - return &StringStringMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// NewStringStringMapFromArray returns an StringStringMap object from given array. -// The param given as the keys of the map, -// and as its corresponding values. -// -// If length of is greater than that of , -// the corresponding overflow map values will be the default value of its type. -func NewStringStringMapFromArray(keys []string, values []string, unsafe...bool) *StringStringMap { - m := make(map[string]string) - l := len(values) - for i, k := range keys { - if i < l { - m[k] = values[i] - } else { - m[k] = "" - } - } - return &StringStringMap{ - m : m, - mu : rwmutex.New(unsafe...), - } -} - -// Iterator iterates the hash map with custom callback function . -// If f returns true, then continue iterating; or false to stop. -func (gm *StringStringMap) Iterator(f func (k string, v string) bool) { - gm.mu.RLock() - defer gm.mu.RUnlock() - for k, v := range gm.m { - if !f(k, v) { - break - } - } -} - -// Clone returns a new hash map with copy of current map data. -func (gm *StringStringMap) Clone() *StringStringMap { - return NewStringStringMapFrom(gm.Map(), !gm.mu.IsSafe()) -} - -// Map returns a copy of the data of the hash map. -func (gm *StringStringMap) Map() map[string]string { - m := make(map[string]string) - gm.mu.RLock() - for k, v := range gm.m { - m[k] = v - } - gm.mu.RUnlock() - return m -} - -// Set sets key-value to the hash map. -func (gm *StringStringMap) Set(key string, val string) { - gm.mu.Lock() - gm.m[key] = val - gm.mu.Unlock() -} - -// BatchSet batch sets key-values to the hash map. -func (gm *StringStringMap) BatchSet(m map[string]string) { - gm.mu.Lock() - for k, v := range m { - gm.m[k] = v - } - gm.mu.Unlock() -} - -// Get returns the value by given . -func (gm *StringStringMap) Get(key string) string { - gm.mu.RLock() - val, _ := gm.m[key] - gm.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 , -// or else just return the existing value. -// -// It returns value with given . -func (gm *StringStringMap) doSetWithLockCheck(key string, value string) string { - gm.mu.Lock() - if v, ok := gm.m[key]; ok { - gm.mu.Unlock() - return v - } - gm.m[key] = value - gm.mu.Unlock() - return value -} - -// GetOrSet returns the value by key, -// or set value with given if not exist and returns this value. -func (gm *StringStringMap) GetOrSet(key string, value string) string { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, value) - } else { - return v - } -} - -// GetOrSetFunc returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -func (gm *StringStringMap) GetOrSetFunc(key string, f func() string) string { - gm.mu.RLock() - v, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - return gm.doSetWithLockCheck(key, f()) - } else { - return v - } -} - -// GetOrSetFuncLock returns the value by key, -// or sets value with return value of callback function if not exist -// and returns this value. -// -// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function -// with mutex.Lock of the hash map. -func (gm *StringStringMap) GetOrSetFuncLock(key string, f func() string) string { - gm.mu.RLock() - val, ok := gm.m[key] - gm.mu.RUnlock() - if !ok { - gm.mu.Lock() - defer gm.mu.Unlock() - if v, ok := gm.m[key]; ok { - return v - } - val = f() - gm.m[key] = val - return val - } else { - return val - } -} - -// SetIfNotExist sets to the map if the does not exist, then return true. -// It returns false if exists, and would be ignored. -func (gm *StringStringMap) SetIfNotExist(key string, value string) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, value) - return true - } - return false -} - -// SetIfNotExistFunc sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -func (gm *StringStringMap) SetIfNotExistFunc(key string, f func() string) bool { - if !gm.Contains(key) { - gm.doSetWithLockCheck(key, f()) - return true - } - return false -} - -// SetIfNotExistFuncLock sets value with return value of callback function , then return true. -// It returns false if exists, and would be ignored. -// -// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that -// it executes function with mutex.Lock of the hash map. -func (gm *StringStringMap) SetIfNotExistFuncLock(key string, f func() string) bool { - if !gm.Contains(key) { - gm.mu.Lock() - defer gm.mu.Unlock() - if _, ok := gm.m[key]; !ok { - gm.m[key] = f() - } - return true - } - return false -} - -// BatchRemove batch deletes values of the map by keys. -func (gm *StringStringMap) BatchRemove(keys []string) { - gm.mu.Lock() - for _, key := range keys { - delete(gm.m, key) - } - gm.mu.Unlock() -} - -// Remove deletes value from map by given , and return this deleted value. -func (gm *StringStringMap) Remove(key string) string { - gm.mu.Lock() - val, exists := gm.m[key] - if exists { - delete(gm.m, key) - } - gm.mu.Unlock() - return val -} - -// Keys returns all keys of the map as a slice. -func (gm *StringStringMap) Keys() []string { - gm.mu.RLock() - keys := make([]string, 0) - for key, _ := range gm.m { - keys = append(keys, key) - } - gm.mu.RUnlock() - return keys -} - -// Values returns all values of the map as a slice. -func (gm *StringStringMap) Values() []string { - gm.mu.RLock() - vals := make([]string, 0) - for _, val := range gm.m { - vals = append(vals, val) - } - gm.mu.RUnlock() - return vals -} - -// Contains checks whether a key exists. -// It returns true if the exists, or else false. -func (gm *StringStringMap) Contains(key string) bool { - gm.mu.RLock() - _, exists := gm.m[key] - gm.mu.RUnlock() - return exists -} - -// Size returns the size of the map. -func (gm *StringStringMap) Size() int { - gm.mu.RLock() - length := len(gm.m) - gm.mu.RUnlock() - return length -} - -// IsEmpty checks whether the map is empty. -// It returns true if map is empty, or else false. -func (gm *StringStringMap) IsEmpty() bool { - gm.mu.RLock() - empty := len(gm.m) == 0 - gm.mu.RUnlock() - return empty -} - -// Clear deletes all data of the map, it will remake a new underlying map data map. -func (gm *StringStringMap) Clear() { - gm.mu.Lock() - gm.m = make(map[string]string) - gm.mu.Unlock() -} - -// LockFunc locks writing with given callback function and mutex.Lock. -func (gm *StringStringMap) LockFunc(f func(m map[string]string)) { - gm.mu.Lock() - defer gm.mu.Unlock() - f(gm.m) -} - -// RLockFunc locks reading with given callback function and mutex.RLock. -func (gm *StringStringMap) RLockFunc(f func(m map[string]string)) { - gm.mu.RLock() - defer gm.mu.RUnlock() - f(gm.m) -} - -// Flip exchanges key-value of the map, it will change key-value to value-key. -func (gm *StringStringMap) Flip() { - gm.mu.Lock() - defer gm.mu.Unlock() - n := make(map[string]string, len(gm.m)) - for k, v := range gm.m { - n[v] = k - } - gm.m = n -} - -// Merge merges two hash maps. -// The map will be merged into the map . -func (gm *StringStringMap) Merge(other *StringStringMap) { - gm.mu.Lock() - defer gm.mu.Unlock() - if other != gm { - other.mu.RLock() - defer other.mu.RUnlock() - } - for k, v := range other.m { - gm.m[k] = v - } -} diff --git a/g/container/gmap/gmap_tree_map.go b/g/container/gmap/gmap_tree_map.go new file mode 100644 index 000000000..7e91f4b49 --- /dev/null +++ b/g/container/gmap/gmap_tree_map.go @@ -0,0 +1,30 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gmap + +import ( + "github.com/gogf/gf/g/container/gtree" +) + +// Map based on red-black tree, alias of RedBlackTree. +type TreeMap = gtree.RedBlackTree + +// NewTreeMap instantiates a tree map with the custom comparator. +// The param 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 { + return gtree.NewRedBlackTree(comparator, unsafe...) +} + +// NewTreeMapFrom instantiates a tree map with the custom comparator and map. +// Note that, the param map will be set as the underlying data map(no deep copy), +// there might be some concurrent-safe issues when changing the map outside. +// The param 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 { + return gtree.NewRedBlackTreeFrom(comparator, data, unsafe...) +} \ No newline at end of file diff --git a/g/container/gmap/gmap_z_interface_interface_basic_test.go b/g/container/gmap/gmap_z_basic_test.go similarity index 88% rename from g/container/gmap/gmap_z_interface_interface_basic_test.go rename to g/container/gmap/gmap_z_basic_test.go index 7d90591a1..e32cd9586 100644 --- a/g/container/gmap/gmap_z_interface_interface_basic_test.go +++ b/g/container/gmap/gmap_z_basic_test.go @@ -1,3 +1,9 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + package gmap_test import ( @@ -9,9 +15,6 @@ import ( func getValue() interface{} { return 3 } -func callBack(k interface{}, v interface{}) bool { - return true -} func Test_Map_Basic(t *testing.T) { gtest.Case(t, func() { @@ -45,9 +48,6 @@ func Test_Map_Basic(t *testing.T) { m2 := gmap.NewFrom(map[interface{}]interface{}{1: 1, "key1": "val1"}) gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) - m3 := gmap.NewFromArray([]interface{}{1, "key1"}, []interface{}{1, "val1"}) - gtest.Assert(m3.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) - }) } func Test_Map_Set_Fun(t *testing.T) { @@ -63,9 +63,9 @@ func Test_Map_Set_Fun(t *testing.T) { func Test_Map_Batch(t *testing.T) { m := gmap.New() - m.BatchSet(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) - m.BatchRemove([]interface{}{"key1", 1}) + m.Removes([]interface{}{"key1", 1}) gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"}) } func Test_Map_Iterator(t *testing.T){ diff --git a/g/container/gmap/gmap_z_bench_maps_test.go b/g/container/gmap/gmap_z_bench_maps_test.go new file mode 100644 index 000000000..5194311d0 --- /dev/null +++ b/g/container/gmap/gmap_z_bench_maps_test.go @@ -0,0 +1,55 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +// go test *.go -bench=".*" -benchmem + +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/util/gutil" + "testing" +) + +var hashMap = gmap.New() +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) + } +} + +func Benchmark_ListMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + listMap.Set(i, i) + } +} + +func Benchmark_TreeMap_Set(b *testing.B) { + for i := 0; i < b.N; i++ { + treeMap.Set(i, i) + } +} + +func Benchmark_HashMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + hashMap.Get(i) + } +} + +func Benchmark_ListMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + listMap.Get(i) + } +} + +func Benchmark_TreeMap_Get(b *testing.B) { + for i := 0; i < b.N; i++ { + treeMap.Get(i) + } +} diff --git a/g/container/gmap/gmap_z_bench_safe_test.go b/g/container/gmap/gmap_z_bench_safe_test.go index f7289af0b..312ba1452 100644 --- a/g/container/gmap/gmap_z_bench_safe_test.go +++ b/g/container/gmap/gmap_z_bench_safe_test.go @@ -6,31 +6,21 @@ // go test *.go -bench=".*" -benchmem -package gmap +package gmap_test import ( - "testing" + "github.com/gogf/gf/g/container/gmap" + "testing" "strconv" ) - -var ibm = NewIntBoolMap() -var iim = NewIntIntMap() -var iifm = NewIntInterfaceMap() -var ism = NewIntStringMap() -var ififm = NewMap() -var sbm = NewStringBoolMap() -var sim = NewStringIntMap() -var sifm = NewStringInterfaceMap() -var ssm = NewStringStringMap() - -// 写入性能测试 - -func Benchmark_IntBoolMap_Set(b *testing.B) { - for i := 0; i < b.N; i++ { - ibm.Set(i, true) - } -} +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() func Benchmark_IntIntMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { @@ -38,56 +28,43 @@ func Benchmark_IntIntMap_Set(b *testing.B) { } } -func Benchmark_IntInterfaceMap_Set(b *testing.B) { +func Benchmark_IntAnyMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { iifm.Set(i, i) } } -func Benchmark_IntStringMap_Set(b *testing.B) { +func Benchmark_IntStrMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { ism.Set(i, strconv.Itoa(i)) } } -func Benchmark_InterfaceInterfaceMap_Set(b *testing.B) { +func Benchmark_AnyAnyMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { ififm.Set(i, i) } } -func Benchmark_StringBoolMap_Set(b *testing.B) { - for i := 0; i < b.N; i++ { - sbm.Set(strconv.Itoa(i), true) - } -} - -func Benchmark_StringIntMap_Set(b *testing.B) { +func Benchmark_StrIntMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { sim.Set(strconv.Itoa(i), i) } } -func Benchmark_StringInterfaceMap_Set(b *testing.B) { +func Benchmark_StrAnyMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { sifm.Set(strconv.Itoa(i), i) } } -func Benchmark_StringStringMap_Set(b *testing.B) { +func Benchmark_StrStrMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { ssm.Set(strconv.Itoa(i), strconv.Itoa(i)) } } -// 读取性能测试 - -func Benchmark_IntBoolMap_Get(b *testing.B) { - for i := 0; i < b.N; i++ { - ibm.Get(i) - } -} func Benchmark_IntIntMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { @@ -95,43 +72,37 @@ func Benchmark_IntIntMap_Get(b *testing.B) { } } -func Benchmark_IntInterfaceMap_Get(b *testing.B) { +func Benchmark_IntAnyMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { iifm.Get(i) } } -func Benchmark_IntStringMap_Get(b *testing.B) { +func Benchmark_IntStrMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { ism.Get(i) } } -func Benchmark_InterfaceInterfaceMap_Get(b *testing.B) { +func Benchmark_AnyAnyMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { ififm.Get(i) } } -func Benchmark_StringBoolMap_Get(b *testing.B) { - for i := 0; i < b.N; i++ { - sbm.Get(strconv.Itoa(i)) - } -} - -func Benchmark_StringIntMap_Get(b *testing.B) { +func Benchmark_StrIntMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { sim.Get(strconv.Itoa(i)) } } -func Benchmark_StringInterfaceMap_Get(b *testing.B) { +func Benchmark_StrAnyMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { sifm.Get(strconv.Itoa(i)) } } -func Benchmark_StringStringMap_Get(b *testing.B) { +func Benchmark_StrStrMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { ssm.Get(strconv.Itoa(i)) } diff --git a/g/container/gmap/gmap_z_bench_unsafe_test.go b/g/container/gmap/gmap_z_bench_unsafe_test.go index 167446d25..5991c408e 100644 --- a/g/container/gmap/gmap_z_bench_unsafe_test.go +++ b/g/container/gmap/gmap_z_bench_unsafe_test.go @@ -6,75 +6,61 @@ // go test *.go -bench=".*" -benchmem -package gmap +package gmap_test import ( - "testing" + "github.com/gogf/gf/g/container/gmap" + "testing" "strconv" ) - -var ibmUnsafe = NewIntBoolMap(true) -var iimUnsafe = NewIntIntMap(true) -var iifmUnsafe = NewIntInterfaceMap(true) -var ismUnsafe = NewIntStringMap(true) -var ififmUnsafe = NewMap(true) -var sbmUnsafe = NewStringBoolMap(true) -var simUnsafe = NewStringIntMap(true) -var sifmUnsafe = NewStringInterfaceMap(true) -var ssmUnsafe = NewStringStringMap(true) +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) // 写入性能测试 -func Benchmark_Unsafe_IntBoolMap_Set(b *testing.B) { - for i := 0; i < b.N; i++ { - ibmUnsafe.Set(i, true) - } -} - func Benchmark_Unsafe_IntIntMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { iimUnsafe.Set(i, i) } } -func Benchmark_Unsafe_IntInterfaceMap_Set(b *testing.B) { +func Benchmark_Unsafe_IntAnyMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { iifmUnsafe.Set(i, i) } } -func Benchmark_Unsafe_IntStringMap_Set(b *testing.B) { +func Benchmark_Unsafe_IntStrMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { ismUnsafe.Set(i, strconv.Itoa(i)) } } -func Benchmark_Unsafe_InterfaceInterfaceMap_Set(b *testing.B) { +func Benchmark_Unsafe_AnyAnyMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { ififmUnsafe.Set(i, i) } } -func Benchmark_Unsafe_StringBoolMap_Set(b *testing.B) { - for i := 0; i < b.N; i++ { - sbmUnsafe.Set(strconv.Itoa(i), true) - } -} - -func Benchmark_Unsafe_StringIntMap_Set(b *testing.B) { +func Benchmark_Unsafe_StrIntMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { simUnsafe.Set(strconv.Itoa(i), i) } } -func Benchmark_Unsafe_StringInterfaceMap_Set(b *testing.B) { +func Benchmark_Unsafe_StrAnyMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { sifmUnsafe.Set(strconv.Itoa(i), i) } } -func Benchmark_Unsafe_StringStringMap_Set(b *testing.B) { +func Benchmark_Unsafe_StrStrMap_Set(b *testing.B) { for i := 0; i < b.N; i++ { ssmUnsafe.Set(strconv.Itoa(i), strconv.Itoa(i)) } @@ -83,11 +69,6 @@ func Benchmark_Unsafe_StringStringMap_Set(b *testing.B) { // 读取性能测试 -func Benchmark_Unsafe_IntBoolMap_Get(b *testing.B) { - for i := 0; i < b.N; i++ { - ibmUnsafe.Get(i) - } -} func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { @@ -95,43 +76,37 @@ func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) { } } -func Benchmark_Unsafe_IntInterfaceMap_Get(b *testing.B) { +func Benchmark_Unsafe_IntAnyMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { iifmUnsafe.Get(i) } } -func Benchmark_Unsafe_IntStringMap_Get(b *testing.B) { +func Benchmark_Unsafe_IntStrMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { ismUnsafe.Get(i) } } -func Benchmark_Unsafe_InterfaceInterfaceMap_Get(b *testing.B) { +func Benchmark_Unsafe_AnyAnyMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { ififmUnsafe.Get(i) } } -func Benchmark_Unsafe_StringBoolMap_Get(b *testing.B) { - for i := 0; i < b.N; i++ { - sbmUnsafe.Get(strconv.Itoa(i)) - } -} - -func Benchmark_Unsafe_StringIntMap_Get(b *testing.B) { +func Benchmark_Unsafe_StrIntMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { simUnsafe.Get(strconv.Itoa(i)) } } -func Benchmark_Unsafe_StringInterfaceMap_Get(b *testing.B) { +func Benchmark_Unsafe_StrAnyMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { sifmUnsafe.Get(strconv.Itoa(i)) } } -func Benchmark_Unsafe_StringStringMap_Get(b *testing.B) { +func Benchmark_Unsafe_StrStrMap_Get(b *testing.B) { for i := 0; i < b.N; i++ { ssmUnsafe.Get(strconv.Itoa(i)) } diff --git a/g/container/gmap/gmap_z_normal_example_test.go b/g/container/gmap/gmap_z_example_test.go similarity index 96% rename from g/container/gmap/gmap_z_normal_example_test.go rename to g/container/gmap/gmap_z_example_test.go index b134c5ff8..67d27ef0b 100644 --- a/g/container/gmap/gmap_z_normal_example_test.go +++ b/g/container/gmap/gmap_z_example_test.go @@ -23,7 +23,7 @@ func Example_Normal_Basic() { fmt.Println(m.Values()) //Batch add data - m.BatchSet(add_map) + m.Sets(add_map) //Gets the value of the corresponding key key3_val := m.Get("key3") @@ -43,7 +43,7 @@ func Example_Normal_Basic() { //Batch remove keys remove_keys := []interface{}{"key1", 1} - m.BatchRemove(remove_keys) + m.Removes(remove_keys) fmt.Println(m.Keys()) //Contains checks whether a key exists. diff --git a/g/container/gmap/gmap_z_int_interface_test.go b/g/container/gmap/gmap_z_int_any_test.go similarity index 58% rename from g/container/gmap/gmap_z_int_interface_test.go rename to g/container/gmap/gmap_z_int_any_test.go index 3bfb47fcf..b42f26b49 100644 --- a/g/container/gmap/gmap_z_int_interface_test.go +++ b/g/container/gmap/gmap_z_int_any_test.go @@ -1,3 +1,9 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + package gmap_test import ( @@ -6,15 +12,15 @@ import ( "testing" ) -func getInterface() interface{} { +func getAny() interface{} { return 123 } -func intInterfaceCallBack(int, interface{}) bool { +func intAnyCallBack(int, interface{}) bool { return true } -func Test_IntInterfaceMap_Basic(t *testing.T) { +func Test_IntAnyMap_Basic(t *testing.T) { gtest.Case(t, func() { - m := gmap.NewIntInterfaceMap() + m := gmap.NewIntAnyMap() m.Set(1, 1) gtest.Assert(m.Get(1), 1) @@ -40,40 +46,37 @@ func Test_IntInterfaceMap_Basic(t *testing.T) { gtest.Assert(m.Size(), 0) gtest.Assert(m.IsEmpty(), true) - m2 := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"}) + m2 := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"}) gtest.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"}) - m3 := gmap.NewIntInterfaceMapFromArray([]int{1, 2}, []interface{}{1, "2"}) - gtest.Assert(m3.Map(), map[int]interface{}{1: 1, 2: "2"}) - }) } -func Test_IntInterfaceMap_Set_Fun(t *testing.T) { - m := gmap.NewIntInterfaceMap() +func Test_IntAnyMap_Set_Fun(t *testing.T) { + m := gmap.NewIntAnyMap() - m.GetOrSetFunc(1, getInterface) - m.GetOrSetFuncLock(2, getInterface) + m.GetOrSetFunc(1, getAny) + m.GetOrSetFuncLock(2, getAny) gtest.Assert(m.Get(1), 123) gtest.Assert(m.Get(2), 123) - gtest.Assert(m.SetIfNotExistFunc(1, getInterface), false) - gtest.Assert(m.SetIfNotExistFunc(3, getInterface), true) + gtest.Assert(m.SetIfNotExistFunc(1, getAny), false) + gtest.Assert(m.SetIfNotExistFunc(3, getAny), true) - gtest.Assert(m.SetIfNotExistFuncLock(2, getInterface), false) - gtest.Assert(m.SetIfNotExistFuncLock(4, getInterface), true) + gtest.Assert(m.SetIfNotExistFuncLock(2, getAny), false) + gtest.Assert(m.SetIfNotExistFuncLock(4, getAny), true) } -func Test_IntInterfaceMap_Batch(t *testing.T) { - m := gmap.NewIntInterfaceMap() +func Test_IntAnyMap_Batch(t *testing.T) { + m := gmap.NewIntAnyMap() - m.BatchSet(map[int]interface{}{1: 1, 2: "2", 3: 3}) + m.Sets(map[int]interface{}{1: 1, 2: "2", 3: 3}) gtest.Assert(m.Map(), map[int]interface{}{1: 1, 2: "2", 3: 3}) - m.BatchRemove([]int{1, 2}) + m.Removes([]int{1, 2}) gtest.Assert(m.Map(), map[int]interface{}{3: 3}) } -func Test_IntInterfaceMap_Iterator(t *testing.T){ +func Test_IntAnyMap_Iterator(t *testing.T){ expect := map[int]interface{}{1: 1, 2: "2"} - m := gmap.NewIntInterfaceMapFrom(expect) + m := gmap.NewIntAnyMapFrom(expect) m.Iterator(func(k int, v interface{}) bool { gtest.Assert(expect[k], v) return true @@ -95,9 +98,9 @@ func Test_IntInterfaceMap_Iterator(t *testing.T){ } -func Test_IntInterfaceMap_Lock(t *testing.T){ +func Test_IntAnyMap_Lock(t *testing.T){ expect := map[int]interface{}{1: 1, 2: "2"} - m := gmap.NewIntInterfaceMapFrom(expect) + m := gmap.NewIntAnyMapFrom(expect) m.LockFunc(func(m map[int]interface{}) { gtest.Assert(m, expect) }) @@ -105,9 +108,9 @@ func Test_IntInterfaceMap_Lock(t *testing.T){ gtest.Assert(m, expect) }) } -func Test_IntInterfaceMap_Clone(t *testing.T) { +func Test_IntAnyMap_Clone(t *testing.T) { //clone 方法是深克隆 - m := gmap.NewIntInterfaceMapFrom(map[int]interface{}{1: 1, 2: "2"}) + m := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"}) m_clone := m.Clone() m.Remove(1) @@ -118,9 +121,9 @@ func Test_IntInterfaceMap_Clone(t *testing.T) { //修改clone map,原 map 不影响 gtest.AssertIN(2, m.Keys()) } -func Test_IntInterfaceMap_Merge(t *testing.T) { - m1 := gmap.NewIntInterfaceMap() - m2 := gmap.NewIntInterfaceMap() +func Test_IntAnyMap_Merge(t *testing.T) { + m1 := gmap.NewIntAnyMap() + m2 := gmap.NewIntAnyMap() m1.Set(1, 1) m2.Set(2, "2") m1.Merge(m2) diff --git a/g/container/gmap/gmap_z_int_bool_test.go b/g/container/gmap/gmap_z_int_bool_test.go deleted file mode 100644 index 75c06b357..000000000 --- a/g/container/gmap/gmap_z_int_bool_test.go +++ /dev/null @@ -1,123 +0,0 @@ -package gmap_test - -import ( - "github.com/gogf/gf/g/container/gmap" - "github.com/gogf/gf/g/test/gtest" - "testing" -) - -func getBool() bool { - return true -} -func intBoolCallBack(int, bool) bool { - return true -} -func Test_IntBoolMap_Basic(t *testing.T) { - gtest.Case(t, func() { - m := gmap.NewIntBoolMap() - m.Set(1, true) - - gtest.Assert(m.Get(1), true) - gtest.Assert(m.Size(), 1) - gtest.Assert(m.IsEmpty(), false) - - gtest.Assert(m.GetOrSet(2, false), false) - gtest.Assert(m.SetIfNotExist(2, false), false) - - gtest.Assert(m.SetIfNotExist(3, false), true) - - gtest.Assert(m.Remove(2), false) - gtest.Assert(m.Contains(2), false) - - gtest.AssertIN(3, m.Keys()) - gtest.AssertIN(1, m.Keys()) - - m.Clear() - gtest.Assert(m.Size(), 0) - gtest.Assert(m.IsEmpty(), true) - - m2 := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false}) - gtest.Assert(m2.Map(), map[int]bool{1: true, 2: false}) - m3 := gmap.NewIntBoolMapFromArray([]int{1, 2}, []bool{true, false}) - gtest.Assert(m3.Map(), map[int]bool{1: true, 2: false}) - - }) -} -func Test_IntBoolMap_Set_Fun(t *testing.T) { - m := gmap.NewIntBoolMap() - - m.GetOrSetFunc(1, getBool) - m.GetOrSetFuncLock(2, getBool) - gtest.Assert(m.Get(1), true) - gtest.Assert(m.Get(2), true) - gtest.Assert(m.SetIfNotExistFunc(1, getBool), false) - gtest.Assert(m.SetIfNotExistFunc(4, getBool), true) - - gtest.Assert(m.SetIfNotExistFuncLock(2, getBool), false) - gtest.Assert(m.SetIfNotExistFuncLock(3, getBool), true) - -} - -func Test_IntBoolMap_Batch(t *testing.T) { - m := gmap.NewIntBoolMap() - - m.BatchSet(map[int]bool{1: true, 2: false, 3: true}) - gtest.Assert(m.Map(), map[int]bool{1: true, 2: false, 3: true}) - m.BatchRemove([]int{1, 2}) - gtest.Assert(m.Map(), map[int]bool{3: true}) -} -func Test_IntBoolMap_Iterator(t *testing.T){ - expect := map[int]bool{1: true, 2: false} - m := gmap.NewIntBoolMapFrom(expect) - m.Iterator(func(k int, v bool) bool { - gtest.Assert(expect[k], v) - return true - }) - // 断言返回值对遍历控制 - i := 0 - j := 0 - m.Iterator(func(k int, v bool) bool { - i++ - return true - }) - m.Iterator(func(k int, v bool) bool { - j++ - return false - }) - gtest.Assert(i, 2) - gtest.Assert(j, 1) -} - -func Test_IntBoolMap_Lock(t *testing.T){ - expect := map[int]bool{1: true, 2: false} - m := gmap.NewIntBoolMapFrom(expect) - m.LockFunc(func(m map[int]bool) { - gtest.Assert(m, expect) - }) - m.RLockFunc(func(m map[int]bool) { - gtest.Assert(m, expect) - }) - -} - -func Test_IntBoolMap_Clone(t *testing.T) { - //clone 方法是深克隆 - m := gmap.NewIntBoolMapFrom(map[int]bool{1: true, 2: false}) - - m_clone := m.Clone() - m.Remove(1) - //修改原 map,clone 后的 map 不影响 - gtest.AssertIN(1, m_clone.Keys()) - - m_clone.Remove(2) - //修改clone map,原 map 不影响 - gtest.AssertIN(2, m.Keys()) -} -func Test_IntBoolMap_Merge(t *testing.T) { - m1 := gmap.NewIntBoolMap() - m2 := gmap.NewIntBoolMap() - m1.Set(1, true) - m2.Set(2, false) - m1.Merge(m2) - gtest.Assert(m1.Map(), map[int]bool{1: true, 2: false}) -} diff --git a/g/container/gmap/gmap_z_int_int_test.go b/g/container/gmap/gmap_z_int_int_test.go index c58437cb4..fee32c642 100644 --- a/g/container/gmap/gmap_z_int_int_test.go +++ b/g/container/gmap/gmap_z_int_int_test.go @@ -1,3 +1,9 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + package gmap_test import ( @@ -42,9 +48,6 @@ func Test_IntIntMap_Basic(t *testing.T) { m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2}) gtest.Assert(m2.Map(), map[int]int{1: 1, 2: 2}) - m3 := gmap.NewIntIntMapFromArray([]int{1, 2}, []int{1, 2}) - gtest.Assert(m3.Map(), map[int]int{1: 1, 2: 2}) - }) } func Test_IntIntMap_Set_Fun(t *testing.T) { @@ -65,10 +68,10 @@ func Test_IntIntMap_Set_Fun(t *testing.T) { func Test_IntIntMap_Batch(t *testing.T) { m := gmap.NewIntIntMap() - m.BatchSet(map[int]int{1: 1, 2: 2, 3: 3}) + m.Sets(map[int]int{1: 1, 2: 2, 3: 3}) m.Iterator(intIntCallBack) gtest.Assert(m.Map(), map[int]int{1: 1, 2: 2, 3: 3}) - m.BatchRemove([]int{1, 2}) + m.Removes([]int{1, 2}) gtest.Assert(m.Map(), map[int]int{3: 3}) } diff --git a/g/container/gmap/gmap_z_int_string_test.go b/g/container/gmap/gmap_z_int_str_test.go similarity index 60% rename from g/container/gmap/gmap_z_int_string_test.go rename to g/container/gmap/gmap_z_int_str_test.go index 287fc7b6f..45c446cab 100644 --- a/g/container/gmap/gmap_z_int_string_test.go +++ b/g/container/gmap/gmap_z_int_str_test.go @@ -1,3 +1,9 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + package gmap_test import ( @@ -6,15 +12,15 @@ import ( "testing" ) -func getString() string { +func getStr() string { return "z" } -func intStringCallBack(int, string) bool { +func intStrCallBack(int, string) bool { return true } -func Test_IntStringMap_Basic(t *testing.T) { +func Test_IntStrMap_Basic(t *testing.T) { gtest.Case(t, func() { - m := gmap.NewIntStringMap() + m := gmap.NewIntStrMap() m.Set(1, "a") gtest.Assert(m.Get(1), "a") @@ -36,7 +42,7 @@ func Test_IntStringMap_Basic(t *testing.T) { //反转之后不成为以下 map,flip 操作只是翻转原 map //gtest.Assert(m.Map(), map[string]int{"a": 1, "c": 3}) - m_f := gmap.NewIntStringMap() + m_f := gmap.NewIntStrMap() m_f.Set(1, "2") m_f.Flip() gtest.Assert(m_f.Map(), map[int]string{2: "1"}) @@ -45,39 +51,36 @@ func Test_IntStringMap_Basic(t *testing.T) { gtest.Assert(m.Size(), 0) gtest.Assert(m.IsEmpty(), true) - m2 := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b"}) + m2 := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b"}) gtest.Assert(m2.Map(), map[int]string{1: "a", 2: "b"}) - m3 := gmap.NewIntStringMapFromArray([]int{1, 2}, []string{"a", "b"}) - gtest.Assert(m3.Map(), map[int]string{1: "a", 2: "b"}) - }) } -func Test_IntStringMap_Set_Fun(t *testing.T) { - m := gmap.NewIntStringMap() +func Test_IntStrMap_Set_Fun(t *testing.T) { + m := gmap.NewIntStrMap() - m.GetOrSetFunc(1, getString) - m.GetOrSetFuncLock(2, getString) + m.GetOrSetFunc(1, getStr) + m.GetOrSetFuncLock(2, getStr) gtest.Assert(m.Get(1), "z") gtest.Assert(m.Get(2), "z") - gtest.Assert(m.SetIfNotExistFunc(1, getString), false) - gtest.Assert(m.SetIfNotExistFunc(3, getString), true) + gtest.Assert(m.SetIfNotExistFunc(1, getStr), false) + gtest.Assert(m.SetIfNotExistFunc(3, getStr), true) - gtest.Assert(m.SetIfNotExistFuncLock(2, getString), false) - gtest.Assert(m.SetIfNotExistFuncLock(4, getString), true) + gtest.Assert(m.SetIfNotExistFuncLock(2, getStr), false) + gtest.Assert(m.SetIfNotExistFuncLock(4, getStr), true) } -func Test_IntStringMap_Batch(t *testing.T) { - m := gmap.NewIntStringMap() +func Test_IntStrMap_Batch(t *testing.T) { + m := gmap.NewIntStrMap() - m.BatchSet(map[int]string{1: "a", 2: "b", 3: "c"}) + m.Sets(map[int]string{1: "a", 2: "b", 3: "c"}) gtest.Assert(m.Map(), map[int]string{1: "a", 2: "b",3: "c"}) - m.BatchRemove([]int{1, 2}) + m.Removes([]int{1, 2}) gtest.Assert(m.Map(), map[int]interface{}{3: "c"}) } -func Test_IntStringMap_Iterator(t *testing.T){ +func Test_IntStrMap_Iterator(t *testing.T){ expect := map[int]string{1: "a", 2: "b"} - m := gmap.NewIntStringMapFrom(expect) + m := gmap.NewIntStrMapFrom(expect) m.Iterator(func(k int, v string) bool { gtest.Assert(expect[k], v) return true @@ -97,10 +100,10 @@ func Test_IntStringMap_Iterator(t *testing.T){ gtest.Assert(j, 1) } -func Test_IntStringMap_Lock(t *testing.T){ +func Test_IntStrMap_Lock(t *testing.T){ expect := map[int]string{1: "a", 2: "b", 3: "c"} - m := gmap.NewIntStringMapFrom(expect) + m := gmap.NewIntStrMapFrom(expect) m.LockFunc(func(m map[int]string) { gtest.Assert(m, expect) }) @@ -109,9 +112,9 @@ func Test_IntStringMap_Lock(t *testing.T){ }) } -func Test_IntStringMap_Clone(t *testing.T) { +func Test_IntStrMap_Clone(t *testing.T) { //clone 方法是深克隆 - m := gmap.NewIntStringMapFrom(map[int]string{1: "a", 2: "b", 3: "c"}) + m := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b", 3: "c"}) m_clone := m.Clone() m.Remove(1) @@ -122,9 +125,9 @@ func Test_IntStringMap_Clone(t *testing.T) { //修改clone map,原 map 不影响 gtest.AssertIN(2, m.Keys()) } -func Test_IntStringMap_Merge(t *testing.T) { - m1 := gmap.NewIntStringMap() - m2 := gmap.NewIntStringMap() +func Test_IntStrMap_Merge(t *testing.T) { + m1 := gmap.NewIntStrMap() + m2 := gmap.NewIntStrMap() m1.Set(1, "a") m2.Set(2, "b") m1.Merge(m2) diff --git a/g/container/gmap/gmap_z_link_map_test.go b/g/container/gmap/gmap_z_link_map_test.go new file mode 100644 index 000000000..312e37cf5 --- /dev/null +++ b/g/container/gmap/gmap_z_link_map_test.go @@ -0,0 +1,120 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gmap_test + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "testing" +) + +func Test_List_Map_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewListMap() + m.Set("key1", "val1") + gtest.Assert(m.Keys(), []interface{}{"key1"}) + + gtest.Assert(m.Get("key1"), "val1") + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("key2", "val2"), "val2") + gtest.Assert(m.SetIfNotExist("key2", "val2"), false) + + gtest.Assert(m.SetIfNotExist("key3", "val3"), true) + gtest.Assert(m.Remove("key2"), "val2") + gtest.Assert(m.Contains("key2"), false) + + gtest.AssertIN("key3", m.Keys()) + gtest.AssertIN("key1", m.Keys()) + gtest.AssertIN("val3", m.Values()) + gtest.AssertIN("val1", m.Values()) + + m.Flip() + + gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"}) + gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) + }) +} +func Test_List_Map_Set_Fun(t *testing.T) { + m := gmap.NewListMap() + m.GetOrSetFunc("fun", getValue) + m.GetOrSetFuncLock("funlock", getValue) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + m.GetOrSetFunc("fun", getValue) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) +} + +func Test_List_Map_Batch(t *testing.T) { + m := gmap.NewListMap() + m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + 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"} + + m := gmap.NewListMapFrom(expect) + m.Iterator(func(k interface{}, v interface{}) bool { + gtest.Assert(expect[k], v) + return true + }) + // 断言返回值对遍历控制 + i := 0 + j := 0 + m.Iterator(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.Iterator(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, 2) + gtest.Assert(j, 1) +} + +func Test_List_Map_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewListMapFrom(map[interface{}]interface{}{1: 1, "key1": "val1"}) + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove("key1") + //修改clone map,原 map 不影响 + gtest.AssertIN("key1", m.Keys()) +} + +func Test_List_Map_Basic_Merge(t *testing.T) { + m1 := gmap.NewListMap() + m2 := gmap.NewListMap() + m1.Set("key1", "val1") + m2.Set("key2", "val2") + m1.Merge(m2) + gtest.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"}) +} + +func Test_List_Map_Order(t *testing.T) { + m := gmap.NewListMap() + m.Set("k1", "v1") + m.Set("k2", "v2") + m.Set("k3", "v3") + gtest.Assert(m.Keys(), g.Slice{"k1", "k2", "k3"}) + gtest.Assert(m.Values(), g.Slice{"v1", "v2", "v3"}) +} diff --git a/g/container/gmap/gmap_z_string_interface_test.go b/g/container/gmap/gmap_z_str_any_test.go similarity index 58% rename from g/container/gmap/gmap_z_string_interface_test.go rename to g/container/gmap/gmap_z_str_any_test.go index dd1319220..29567bc77 100644 --- a/g/container/gmap/gmap_z_string_interface_test.go +++ b/g/container/gmap/gmap_z_str_any_test.go @@ -1,3 +1,9 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + package gmap_test import ( @@ -6,12 +12,12 @@ import ( "testing" ) -func stringInterfaceCallBack(string, interface{}) bool { +func stringAnyCallBack(string, interface{}) bool { return true } -func Test_StringInterfaceMap_Basic(t *testing.T) { +func Test_StrAnyMap_Basic(t *testing.T) { gtest.Case(t, func() { - m := gmap.NewStringInterfaceMap() + m := gmap.NewStrAnyMap() m.Set("a", 1) gtest.Assert(m.Get("a"), 1) @@ -38,40 +44,37 @@ func Test_StringInterfaceMap_Basic(t *testing.T) { gtest.Assert(m.Size(), 0) gtest.Assert(m.IsEmpty(), true) - m2 := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"}) + m2 := gmap.NewStrAnyMapFrom(map[string]interface{}{"a": 1, "b": "2"}) gtest.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"}) - m3 := gmap.NewStringInterfaceMapFromArray([]string{"a", "b"}, []interface{}{1, "2"}) - gtest.Assert(m3.Map(), map[string]interface{}{"a": 1, "b": "2"}) - }) } -func Test_StringInterfaceMap_Set_Fun(t *testing.T) { - m := gmap.NewStringInterfaceMap() +func Test_StrAnyMap_Set_Fun(t *testing.T) { + m := gmap.NewStrAnyMap() - m.GetOrSetFunc("a", getInterface) - m.GetOrSetFuncLock("b", getInterface) + m.GetOrSetFunc("a", getAny) + m.GetOrSetFuncLock("b", getAny) gtest.Assert(m.Get("a"), 123) gtest.Assert(m.Get("b"), 123) - gtest.Assert(m.SetIfNotExistFunc("a", getInterface), false) - gtest.Assert(m.SetIfNotExistFunc("c", getInterface), true) + gtest.Assert(m.SetIfNotExistFunc("a", getAny), false) + gtest.Assert(m.SetIfNotExistFunc("c", getAny), true) - gtest.Assert(m.SetIfNotExistFuncLock("b", getInterface), false) - gtest.Assert(m.SetIfNotExistFuncLock("d", getInterface), true) + gtest.Assert(m.SetIfNotExistFuncLock("b", getAny), false) + gtest.Assert(m.SetIfNotExistFuncLock("d", getAny), true) } -func Test_StringInterfaceMap_Batch(t *testing.T) { - m := gmap.NewStringInterfaceMap() +func Test_StrAnyMap_Batch(t *testing.T) { + m := gmap.NewStrAnyMap() - m.BatchSet(map[string]interface{}{"a": 1, "b": "2", "c": 3}) + m.Sets(map[string]interface{}{"a": 1, "b": "2", "c": 3}) gtest.Assert(m.Map(), map[string]interface{}{"a": 1, "b": "2", "c": 3}) - m.BatchRemove([]string{"a", "b"}) + m.Removes([]string{"a", "b"}) gtest.Assert(m.Map(), map[string]interface{}{"c": 3}) } -func Test_StringInterfaceMap_Iterator(t *testing.T) { +func Test_StrAnyMap_Iterator(t *testing.T) { expect := map[string]interface{}{"a": true, "b": false} - m := gmap.NewStringInterfaceMapFrom(expect) + m := gmap.NewStrAnyMapFrom(expect) m.Iterator(func(k string, v interface{}) bool { gtest.Assert(expect[k], v) return true @@ -91,10 +94,10 @@ func Test_StringInterfaceMap_Iterator(t *testing.T) { gtest.Assert(j, 1) } -func Test_StringInterfaceMap_Lock(t *testing.T) { +func Test_StrAnyMap_Lock(t *testing.T) { expect := map[string]interface{}{"a": true, "b": false} - m := gmap.NewStringInterfaceMapFrom(expect) + m := gmap.NewStrAnyMapFrom(expect) m.LockFunc(func(m map[string]interface{}) { gtest.Assert(m, expect) }) @@ -102,9 +105,9 @@ func Test_StringInterfaceMap_Lock(t *testing.T) { gtest.Assert(m, expect) }) } -func Test_StringInterfaceMap_Clone(t *testing.T) { +func Test_StrAnyMap_Clone(t *testing.T) { //clone 方法是深克隆 - m := gmap.NewStringInterfaceMapFrom(map[string]interface{}{"a": 1, "b": "2"}) + m := gmap.NewStrAnyMapFrom(map[string]interface{}{"a": 1, "b": "2"}) m_clone := m.Clone() m.Remove("a") @@ -115,9 +118,9 @@ func Test_StringInterfaceMap_Clone(t *testing.T) { //修改clone map,原 map 不影响 gtest.AssertIN("b", m.Keys()) } -func Test_StringInterfaceMap_Merge(t *testing.T) { - m1 := gmap.NewStringInterfaceMap() - m2 := gmap.NewStringInterfaceMap() +func Test_StrAnyMap_Merge(t *testing.T) { + m1 := gmap.NewStrAnyMap() + m2 := gmap.NewStrAnyMap() m1.Set("a", 1) m2.Set("b", "2") m1.Merge(m2) diff --git a/g/container/gmap/gmap_z_string_int_test.go b/g/container/gmap/gmap_z_str_int_test.go similarity index 68% rename from g/container/gmap/gmap_z_string_int_test.go rename to g/container/gmap/gmap_z_str_int_test.go index fad493c19..4bc4a6c89 100644 --- a/g/container/gmap/gmap_z_string_int_test.go +++ b/g/container/gmap/gmap_z_str_int_test.go @@ -1,3 +1,9 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + package gmap_test import ( @@ -9,9 +15,9 @@ import ( func stringIntCallBack(string, int) bool { return true } -func Test_StringIntMap_Basic(t *testing.T) { +func Test_StrIntMap_Basic(t *testing.T) { gtest.Case(t, func() { - m := gmap.NewStringIntMap() + m := gmap.NewStrIntMap() m.Set("a", 1) gtest.Assert(m.Get("a"), 1) @@ -31,7 +37,7 @@ func Test_StringIntMap_Basic(t *testing.T) { gtest.AssertIN(3, m.Values()) gtest.AssertIN(1, m.Values()) - m_f := gmap.NewStringIntMap() + m_f := gmap.NewStrIntMap() m_f.Set("1", 2) m_f.Flip() gtest.Assert(m_f.Map(), map[string]int{"2": 1}) @@ -40,15 +46,12 @@ func Test_StringIntMap_Basic(t *testing.T) { gtest.Assert(m.Size(), 0) gtest.Assert(m.IsEmpty(), true) - m2 := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2}) + m2 := gmap.NewStrIntMapFrom(map[string]int{"a": 1, "b": 2}) gtest.Assert(m2.Map(), map[string]int{"a": 1, "b": 2}) - m3 := gmap.NewStringIntMapFromArray([]string{"a", "b"}, []int{1, 2}) - gtest.Assert(m3.Map(), map[string]int{"a": 1, "b": 2}) - }) } -func Test_StringIntMap_Set_Fun(t *testing.T) { - m := gmap.NewStringIntMap() +func Test_StrIntMap_Set_Fun(t *testing.T) { + m := gmap.NewStrIntMap() m.GetOrSetFunc("a", getInt) m.GetOrSetFuncLock("b", getInt) @@ -62,17 +65,17 @@ func Test_StringIntMap_Set_Fun(t *testing.T) { } -func Test_StringIntMap_Batch(t *testing.T) { - m := gmap.NewStringIntMap() +func Test_StrIntMap_Batch(t *testing.T) { + m := gmap.NewStrIntMap() - m.BatchSet(map[string]int{"a": 1, "b": 2, "c": 3}) + m.Sets(map[string]int{"a": 1, "b": 2, "c": 3}) gtest.Assert(m.Map(), map[string]int{"a": 1, "b": 2, "c": 3}) - m.BatchRemove([]string{"a", "b"}) + m.Removes([]string{"a", "b"}) gtest.Assert(m.Map(), map[string]int{"c": 3}) } -func Test_StringIntMap_Iterator(t *testing.T) { +func Test_StrIntMap_Iterator(t *testing.T) { expect := map[string]int{"a": 1, "b": 2} - m := gmap.NewStringIntMapFrom(expect) + m := gmap.NewStrIntMapFrom(expect) m.Iterator(func(k string, v int) bool { gtest.Assert(expect[k], v) return true @@ -93,10 +96,10 @@ func Test_StringIntMap_Iterator(t *testing.T) { } -func Test_StringIntMap_Lock(t *testing.T) { +func Test_StrIntMap_Lock(t *testing.T) { expect := map[string]int{"a": 1, "b": 2} - m := gmap.NewStringIntMapFrom(expect) + m := gmap.NewStrIntMapFrom(expect) m.LockFunc(func(m map[string]int) { gtest.Assert(m, expect) }) @@ -105,9 +108,9 @@ func Test_StringIntMap_Lock(t *testing.T) { }) } -func Test_StringIntMap_Clone(t *testing.T) { +func Test_StrIntMap_Clone(t *testing.T) { //clone 方法是深克隆 - m := gmap.NewStringIntMapFrom(map[string]int{"a": 1, "b": 2, "c": 3}) + m := gmap.NewStrIntMapFrom(map[string]int{"a": 1, "b": 2, "c": 3}) m_clone := m.Clone() m.Remove("a") @@ -118,9 +121,9 @@ func Test_StringIntMap_Clone(t *testing.T) { //修改clone map,原 map 不影响 gtest.AssertIN("b", m.Keys()) } -func Test_StringIntMap_Merge(t *testing.T) { - m1 := gmap.NewStringIntMap() - m2 := gmap.NewStringIntMap() +func Test_StrIntMap_Merge(t *testing.T) { + m1 := gmap.NewStrIntMap() + m2 := gmap.NewStrIntMap() m1.Set("a", 1) m2.Set("b", 2) m1.Merge(m2) diff --git a/g/container/gmap/gmap_z_string_string_test.go b/g/container/gmap/gmap_z_str_str_test.go similarity index 59% rename from g/container/gmap/gmap_z_string_string_test.go rename to g/container/gmap/gmap_z_str_str_test.go index 77caab856..6d700729d 100644 --- a/g/container/gmap/gmap_z_string_string_test.go +++ b/g/container/gmap/gmap_z_str_str_test.go @@ -1,3 +1,9 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + package gmap_test import ( @@ -6,12 +12,12 @@ import ( "testing" ) -func stringStringCallBack(string, string) bool { +func stringStrCallBack(string, string) bool { return true } -func Test_StringStringMap_Basic(t *testing.T) { +func Test_StrStrMap_Basic(t *testing.T) { gtest.Case(t, func() { - m := gmap.NewStringStringMap() + m := gmap.NewStrStrMap() m.Set("a", "a") gtest.Assert(m.Get("a"), "a") @@ -39,39 +45,36 @@ func Test_StringStringMap_Basic(t *testing.T) { gtest.Assert(m.Size(), 0) gtest.Assert(m.IsEmpty(), true) - m2 := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b"}) + m2 := gmap.NewStrStrMapFrom(map[string]string{"a": "a", "b": "b"}) gtest.Assert(m2.Map(), map[string]string{"a": "a", "b": "b"}) - m3 := gmap.NewStringStringMapFromArray([]string{"a", "b"}, []string{"a", "b"}) - gtest.Assert(m3.Map(), map[string]string{"a": "a", "b": "b"}) - }) } -func Test_StringStringMap_Set_Fun(t *testing.T) { - m := gmap.NewStringStringMap() +func Test_StrStrMap_Set_Fun(t *testing.T) { + m := gmap.NewStrStrMap() - m.GetOrSetFunc("a", getString) - m.GetOrSetFuncLock("b", getString) + m.GetOrSetFunc("a", getStr) + m.GetOrSetFuncLock("b", getStr) gtest.Assert(m.Get("a"), "z") gtest.Assert(m.Get("b"), "z") - gtest.Assert(m.SetIfNotExistFunc("a", getString), false) - gtest.Assert(m.SetIfNotExistFunc("c", getString), true) + gtest.Assert(m.SetIfNotExistFunc("a", getStr), false) + gtest.Assert(m.SetIfNotExistFunc("c", getStr), true) - gtest.Assert(m.SetIfNotExistFuncLock("b", getString), false) - gtest.Assert(m.SetIfNotExistFuncLock("d", getString), true) + gtest.Assert(m.SetIfNotExistFuncLock("b", getStr), false) + gtest.Assert(m.SetIfNotExistFuncLock("d", getStr), true) } -func Test_StringStringMap_Batch(t *testing.T) { - m := gmap.NewStringStringMap() +func Test_StrStrMap_Batch(t *testing.T) { + m := gmap.NewStrStrMap() - m.BatchSet(map[string]string{"a": "a", "b": "b", "c": "c"}) + m.Sets(map[string]string{"a": "a", "b": "b", "c": "c"}) gtest.Assert(m.Map(), map[string]string{"a": "a", "b": "b", "c": "c"}) - m.BatchRemove([]string{"a", "b"}) + m.Removes([]string{"a", "b"}) gtest.Assert(m.Map(), map[string]string{"c": "c"}) } -func Test_StringStringMap_Iterator(t *testing.T) { +func Test_StrStrMap_Iterator(t *testing.T) { expect := map[string]string{"a": "a", "b": "b"} - m := gmap.NewStringStringMapFrom(expect) + m := gmap.NewStrStrMapFrom(expect) m.Iterator(func(k string, v string) bool { gtest.Assert(expect[k], v) return true @@ -91,10 +94,10 @@ func Test_StringStringMap_Iterator(t *testing.T) { gtest.Assert(j, 1) } -func Test_StringStringMap_Lock(t *testing.T) { +func Test_StrStrMap_Lock(t *testing.T) { expect := map[string]string{"a": "a", "b": "b"} - m := gmap.NewStringStringMapFrom(expect) + m := gmap.NewStrStrMapFrom(expect) m.LockFunc(func(m map[string]string) { gtest.Assert(m, expect) }) @@ -102,9 +105,9 @@ func Test_StringStringMap_Lock(t *testing.T) { gtest.Assert(m, expect) }) } -func Test_StringStringMap_Clone(t *testing.T) { +func Test_StrStrMap_Clone(t *testing.T) { //clone 方法是深克隆 - m := gmap.NewStringStringMapFrom(map[string]string{"a": "a", "b": "b", "c": "c"}) + m := gmap.NewStrStrMapFrom(map[string]string{"a": "a", "b": "b", "c": "c"}) m_clone := m.Clone() m.Remove("a") @@ -115,9 +118,9 @@ func Test_StringStringMap_Clone(t *testing.T) { //修改clone map,原 map 不影响 gtest.AssertIN("b", m.Keys()) } -func Test_StringStringMap_Merge(t *testing.T) { - m1 := gmap.NewStringStringMap() - m2 := gmap.NewStringStringMap() +func Test_StrStrMap_Merge(t *testing.T) { + m1 := gmap.NewStrStrMap() + m2 := gmap.NewStrStrMap() m1.Set("a", "a") m2.Set("b", "b") m1.Merge(m2) diff --git a/g/container/gmap/gmap_z_string_bool_test.go b/g/container/gmap/gmap_z_string_bool_test.go deleted file mode 100644 index 3e0f2a1c0..000000000 --- a/g/container/gmap/gmap_z_string_bool_test.go +++ /dev/null @@ -1,122 +0,0 @@ -package gmap_test - -import ( - "github.com/gogf/gf/g/container/gmap" - "github.com/gogf/gf/g/test/gtest" - "testing" -) - -func StringBoolCallBack(string, bool) bool { - return true -} -func Test_StringBoolMap_Basic(t *testing.T) { - gtest.Case(t, func() { - m := gmap.NewStringBoolMap() - m.Set("a", true) - - gtest.Assert(m.Get("a"), true) - gtest.Assert(m.Size(), 1) - gtest.Assert(m.IsEmpty(), false) - - gtest.Assert(m.GetOrSet("b", false), false) - gtest.Assert(m.SetIfNotExist("b", false), false) - - gtest.Assert(m.SetIfNotExist("c", false), true) - - gtest.Assert(m.Remove("b"), false) - gtest.Assert(m.Contains("b"), false) - - gtest.AssertIN("c", m.Keys()) - gtest.AssertIN("a", m.Keys()) - - m.Clear() - gtest.Assert(m.Size(), 0) - gtest.Assert(m.IsEmpty(), true) - - m2 := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false}) - gtest.Assert(m2.Map(), map[string]bool{"a": true, "b": false}) - m3 := gmap.NewStringBoolMapFromArray([]string{"a", "b"}, []bool{true, false}) - gtest.Assert(m3.Map(), map[string]bool{"a": true, "b": false}) - - }) -} -func Test_StringBoolMap_Set_Fun(t *testing.T) { - m := gmap.NewStringBoolMap() - - m.GetOrSetFunc("a", getBool) - m.GetOrSetFuncLock("b", getBool) - gtest.Assert(m.Get("a"), true) - gtest.Assert(m.Get("b"), true) - gtest.Assert(m.SetIfNotExistFunc("a", getBool), false) - gtest.Assert(m.SetIfNotExistFunc("c", getBool), true) - - gtest.Assert(m.SetIfNotExistFuncLock("b", getBool), false) - gtest.Assert(m.SetIfNotExistFuncLock("d", getBool), true) - -} - -func Test_StringBoolMap_Batch(t *testing.T) { - m := gmap.NewStringBoolMap() - - m.BatchSet(map[string]bool{"a": true, "b": false, "c": true}) - gtest.Assert(m.Map(), map[string]bool{"a": true, "b": false, "c": true}) - m.BatchRemove([]string{"a", "b"}) - gtest.Assert(m.Map(), map[string]bool{"c": true}) -} - -func Test_StringBoolMap_Iterator(t *testing.T) { - expect := map[string]bool{"a": true, "b": false} - m := gmap.NewStringBoolMapFrom(expect) - m.Iterator(func(k string, v bool) bool { - gtest.Assert(expect[k], v) - return true - }) - // 断言返回值对遍历控制 - i := 0 - j := 0 - m.Iterator(func(k string, v bool) bool { - i++ - return true - }) - m.Iterator(func(k string, v bool) bool { - j++ - return false - }) - gtest.Assert(i, 2) - gtest.Assert(j, 1) - -} - -func Test_StringBoolMap_Lock(t *testing.T) { - expect := map[string]bool{"a": true, "b": false} - - m := gmap.NewStringBoolMapFrom(expect) - m.LockFunc(func(m map[string]bool) { - gtest.Assert(m, expect) - }) - m.RLockFunc(func(m map[string]bool) { - gtest.Assert(m, expect) - }) -} - -func Test_StringBoolMap_Clone(t *testing.T) { - //clone 方法是深克隆 - m := gmap.NewStringBoolMapFrom(map[string]bool{"a": true, "b": false}) - - m_clone := m.Clone() - m.Remove("a") - //修改原 map,clone 后的 map 不影响 - gtest.AssertIN("a", m_clone.Keys()) - - m_clone.Remove("b") - //修改clone map,原 map 不影响 - gtest.AssertIN("b", m.Keys()) -} -func Test_StringBoolMap_Merge(t *testing.T) { - m1 := gmap.NewStringBoolMap() - m2 := gmap.NewStringBoolMap() - m1.Set("a", true) - m2.Set("b", false) - m1.Merge(m2) - gtest.Assert(m1.Map(), map[string]bool{"a": true, "b": false}) -} diff --git a/g/container/gmap/gmap_z_tree_map_test.go b/g/container/gmap/gmap_z_tree_map_test.go new file mode 100644 index 000000000..d7322e7cb --- /dev/null +++ b/g/container/gmap/gmap_z_tree_map_test.go @@ -0,0 +1,103 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gmap_test + +import ( + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/util/gutil" + "testing" +) + + +func Test_Tree_Map_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gmap.NewTreeMap(gutil.ComparatorString) + m.Set("key1", "val1") + gtest.Assert(m.Keys(), []interface{}{"key1"}) + + gtest.Assert(m.Get("key1"), "val1") + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("key2", "val2"), "val2") + gtest.Assert(m.SetIfNotExist("key2", "val2"), false) + + gtest.Assert(m.SetIfNotExist("key3", "val3"), true) + + gtest.Assert(m.Remove("key2"), "val2") + gtest.Assert(m.Contains("key2"), false) + + gtest.AssertIN("key3", m.Keys()) + gtest.AssertIN("key1", m.Keys()) + gtest.AssertIN("val3", m.Values()) + gtest.AssertIN("val1", m.Values()) + + m.Flip() + gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gmap.NewTreeMapFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"}) + gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) + }) +} +func Test_Tree_Map_Set_Fun(t *testing.T) { + m := gmap.NewTreeMap(gutil.ComparatorString) + m.GetOrSetFunc("fun", getValue) + m.GetOrSetFuncLock("funlock", getValue) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + m.GetOrSetFunc("fun", getValue) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) +} + +func Test_Tree_Map_Batch(t *testing.T) { + m := gmap.NewTreeMap(gutil.ComparatorString) + m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + m.Removes([]interface{}{"key1", 1}) + gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"}) +} +func Test_Tree_Map_Iterator(t *testing.T){ + expect := map[interface{}]interface{}{1: 1, "key1": "val1"} + + m := gmap.NewTreeMapFrom(gutil.ComparatorString, expect) + m.Iterator(func(k interface{}, v interface{}) bool { + gtest.Assert(expect[k], v) + return true + }) + // 断言返回值对遍历控制 + i := 0 + j := 0 + m.Iterator(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.Iterator(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, 2) + gtest.Assert(j, 1) +} + +func Test_Tree_Map_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gmap.NewTreeMapFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"}) + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove("key1") + //修改clone map,原 map 不影响 + gtest.AssertIN("key1", m.Keys()) +} \ No newline at end of file diff --git a/g/container/gpool/gpool.go b/g/container/gpool/gpool.go index e61b97b94..69bbfc6cc 100644 --- a/g/container/gpool/gpool.go +++ b/g/container/gpool/gpool.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gpool provides a object-reusable concurrent-safe pool. +// Package gpool provides object-reusable concurrent-safe pool. package gpool import ( @@ -16,31 +16,38 @@ import ( "time" ) -// 对象池 +// Object-Reusable Pool. type Pool struct { - list *glist.List // 可用/闲置的文件指针链表 - closed *gtype.Bool // 连接池是否已关闭 - Expire int64 // (毫秒)闲置最大时间,超过该时间则被系统回收 - NewFunc func()(interface{}, error) // 创建对象的方法定义 - ExpireFunc func(interface{}) // 对象的过期销毁方法(当池对象销毁需要执行额外的销毁操作时,需要定义该方法) - // 例如: net.Conn, os.File等对象都需要执行额外关闭操作 + 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 // (毫秒)过期时间 - value interface{} // 对象值 + expire int64 // Expire time(millisecond). + value interface{} // Value. } -// 对象创建方法类型 +// Creation function for object. type NewFunc func() (interface{}, error) -// 对象过期方法类型 +// Destruction function for object. type ExpireFunc func(interface{}) -// 创建一个对象池,为保证执行效率,过期时间一旦设定之后无法修改 -// expire = 0表示不过期,expire < 0表示使用完立即回收,expire > 0表示超时回收 -// 注意过期时间单位为**毫秒** + +// New returns a new object pool. +// To ensure execution efficiency, the expiration time cannot be modified once it is set. +// Expire: +// expire = 0 : not expired; +// expire < 0 : immediate recovery after use; +// expire > 0 : timeout recovery; +// Note that the expiration time unit is ** milliseconds **. func New(expire int, newFunc NewFunc, expireFunc...ExpireFunc) *Pool { r := &Pool { list : glist.New(), @@ -55,7 +62,7 @@ func New(expire int, newFunc NewFunc, expireFunc...ExpireFunc) *Pool { return r } -// 放一个临时对象到池中 +// Put puts an item to pool. func (p *Pool) Put(value interface{}) { item := &poolItem { value : value, @@ -68,12 +75,12 @@ func (p *Pool) Put(value interface{}) { p.list.PushBack(item) } -// 清空对象池 +// Clear clears pool, which means it will remove all items from pool. func (p *Pool) Clear() { 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 { @@ -91,17 +98,17 @@ func (p *Pool) Get() (interface{}, error) { 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() } -// 关闭池 +// Close closes the pool. func (p *Pool) Close() { p.closed.Set(true) } -// 超时检测循环 +// checkExpire secondly removes expired items from pool. func (p *Pool) checkExpire() { if p.closed.Val() { gtimer.Exit() diff --git a/g/container/gqueue/gqueue.go b/g/container/gqueue/gqueue.go index d04584e80..ea99c0c6f 100644 --- a/g/container/gqueue/gqueue.go +++ b/g/container/gqueue/gqueue.go @@ -36,8 +36,9 @@ const ( gDEFAULT_QUEUE_SIZE = 10000 ) -// New returns a queue object. -// Param is optional and it is not limited by default. +// New returns an empty queue object. +// Optional parameter is used to limit the size of the queue, which is unlimited by default. +// When is given, the queue will be static and high performance which is comparable with stdlib chan. func New(limit...int) *Queue { q := &Queue { closed : make(chan struct{}, 0), @@ -84,7 +85,7 @@ func (q *Queue) startAsyncLoop() { } // Push pushes the data into the queue. -// Note that it would panics if the Push method is called after the queue is closed. +// 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 @@ -95,14 +96,14 @@ func (q *Queue) Push(v interface{}) { } // Pop pops an item from the queue in FIFO way. -// Note that it would return nil immediately if the Pop method is called after the queue is closed. +// Note that it would return nil immediately if Pop is called after the queue is closed. func (q *Queue) Pop() interface{} { return <- q.C } // Close closes the queue. -// Notice: It would notify all goroutines exit immediately, -// which are blocked reading by Pop method). +// Notice: It would notify all goroutines return immediately, +// which are being blocked reading by Pop method. func (q *Queue) Close() { close(q.C) close(q.events) diff --git a/g/container/gring/gring.go b/g/container/gring/gring.go index 35479669d..0f954771e 100644 --- a/g/container/gring/gring.go +++ b/g/container/gring/gring.go @@ -14,11 +14,12 @@ import ( ) type Ring struct { - mu *rwmutex.RWMutex // 互斥锁 - ring *ring.Ring // 底层环形数据结构 - len *gtype.Int // 数据大小(已使用的大小) - cap *gtype.Int // 总长度(分配的环大小,包括未使用的数据项数量) - dirty *gtype.Bool // 标记环是否脏了(需要重新计算大小,当环大小发生改变时做标记) + 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 { @@ -31,7 +32,7 @@ func New(cap int, unsafe...bool) *Ring { } } -// 返回当前环指向的数据项值 +// Val returns the item's value of current position. func (r *Ring) Val() interface{} { r.mu.RLock() v := r.ring.Value @@ -39,19 +40,19 @@ func (r *Ring) Val() interface{} { return v } -// 返回当前环已有数据项大小 +// Len returns the size of ring. func (r *Ring) Len() int { r.checkAndUpdateLenAndCap() return r.len.Val() } -// 返回当前环总大小(包含未使用长度) +// Cap returns the capacity of ring. func (r *Ring) Cap() int { r.checkAndUpdateLenAndCap() return r.cap.Val() } -// 检测并执行len和cap的更新(两者必须一起更新) +// Checks and updates the len and cap of ring when ring is dirty. func (r *Ring) checkAndUpdateLenAndCap() { if !r.dirty.Val() { return @@ -73,7 +74,7 @@ func (r *Ring) checkAndUpdateLenAndCap() { 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 { @@ -84,7 +85,7 @@ func (r *Ring) Set(value interface{}) *Ring { return r } -// Set & Next +// Put sets 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 { @@ -96,7 +97,8 @@ func (r *Ring) Put(value interface{}) *Ring { return r } -// 环往后(n > 0)或者往前(n < 0)移动n个元素 +// 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) @@ -104,7 +106,7 @@ func (r *Ring) Move(n int) *Ring { return r } -// 环往前移动1个元素 +// Prev returns the previous ring element. r must not be empty. func (r *Ring) Prev() *Ring { r.mu.Lock() r.ring = r.ring.Prev() @@ -112,7 +114,7 @@ func (r *Ring) Prev() *Ring { return r } -// 环往后移动1个元素 +// Next returns the next ring element. r must not be empty. func (r *Ring) Next() *Ring { r.mu.Lock() r.ring = r.ring.Next() @@ -120,11 +122,22 @@ func (r *Ring) Next() *Ring { return r } -// 连接两个环,两个环的大小和位置都有可能会发生改变。 -// 1、链接将环r与环s连接,使得r.Next()成为s并返回r.Next()的原始值。r一定不能为空。 -// 2、如果r和s指向同一个环,则链接它们会从环中移除r和s之间的元素。 -// 删除的元素形成子环,结果是对该子环的引用(如果没有删除元素,结果仍然是r.Next()的原始值,而不是nil)。 -// 3、如果r和s指向不同的环,则链接它们会创建一个单独的环,并在r之后插入s的元素。 结果指向插入后s的最后一个元素后面的元素。 +// Link connects ring r with ring s such that r.Next() +// becomes s and returns the original value for r.Next(). +// r must not be empty. +// +// If r and s point to the same ring, linking +// them removes the elements between r and s from the ring. +// The removed elements form a subring and the result is a +// reference to that subring (if no elements were removed, +// the result is still the original value for r.Next(), +// and not nil). +// +// If r and s point to different rings, linking +// them creates a single ring with the elements of s inserted +// after r. The result points to the element following the +// last element of s after insertion. +// func (r *Ring) Link(s *Ring) *Ring { r.mu.Lock() s.mu.Lock() @@ -136,7 +149,10 @@ func (r *Ring) Link(s *Ring) *Ring { return r } -// 删除环中当前位置往后的n个数据项 +// Unlink removes n % r.Len() elements from the ring r, starting +// at r.Next(). If n % r.Len() == 0, r remains unchanged. +// 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) @@ -145,7 +161,9 @@ func (r *Ring) Unlink(n int) *Ring { return r } -// 读锁遍历,往后只读遍历,回调函数返回true表示继续遍历,否则退出遍历 +// RLockIteratorNext iterates and locks reading forward +// with given callback function within RWMutex.RLock. +// If 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() @@ -159,7 +177,9 @@ func (r *Ring) RLockIteratorNext(f func(value interface{}) bool) { } } -// 读锁遍历,往前只读遍历,回调函数返回true表示继续遍历,否则退出遍历 +// RLockIteratorPrev iterates and locks reading backward +// with given callback function within RWMutex.RLock. +// If 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() @@ -173,7 +193,9 @@ func (r *Ring) RLockIteratorPrev(f func(value interface{}) bool) { } } -// 写锁遍历,往后写遍历,回调函数返回true表示继续遍历,否则退出遍历 +// LockIteratorNext iterates and locks writing forward +// with given callback function within RWMutex.RLock. +// If 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() @@ -187,7 +209,9 @@ func (r *Ring) LockIteratorNext(f func(item *ring.Ring) bool) { } } -// 写锁遍历,往前写遍历,回调函数返回true表示继续遍历,否则退出遍历 +// LockIteratorPrev iterates and locks writing backward +// with given callback function within RWMutex.RLock. +// If 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() @@ -201,7 +225,7 @@ func (r *Ring) LockIteratorPrev(f func(item *ring.Ring) bool) { } } -// 从当前位置,往后只读完整遍历,返回非空数据项值构成的数组 +// 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() @@ -217,7 +241,7 @@ func (r *Ring) SliceNext() []interface{} { 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() diff --git a/g/container/gset/gset.go b/g/container/gset/gset.go index 7f01bc45d..8c02bd33b 100644 --- a/g/container/gset/gset.go +++ b/g/container/gset/gset.go @@ -125,19 +125,17 @@ func (set *Set) String() string { } // LockFunc locks writing with callback function . -func (set *Set) LockFunc(f func(m map[interface{}]struct{})) *Set { +func (set *Set) LockFunc(f func(m map[interface{}]struct{})) { set.mu.Lock() defer set.mu.Unlock() f(set.m) - return set } // RLockFunc locks reading with callback function . -func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) *Set { +func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) { set.mu.RLock() defer set.mu.RUnlock() f(set.m) - return set } // Equal checks whether the two sets equal. diff --git a/g/container/gset/gset_int_set.go b/g/container/gset/gset_int_set.go index 6e018d5fa..338952ff8 100644 --- a/g/container/gset/gset_int_set.go +++ b/g/container/gset/gset_int_set.go @@ -119,19 +119,17 @@ func (set *IntSet) String() string { } // LockFunc locks writing with callback function . -func (set *IntSet) LockFunc(f func(m map[int]struct{})) *IntSet { +func (set *IntSet) LockFunc(f func(m map[int]struct{})) { set.mu.Lock() defer set.mu.Unlock() f(set.m) - return set } // RLockFunc locks reading with callback function . -func (set *IntSet) RLockFunc(f func(m map[int]struct{})) *IntSet { +func (set *IntSet) RLockFunc(f func(m map[int]struct{})) { set.mu.RLock() defer set.mu.RUnlock() f(set.m) - return set } // Equal checks whether the two sets equal. diff --git a/g/container/gset/gset_string_set.go b/g/container/gset/gset_string_set.go index 4664e5808..7a5d20da4 100644 --- a/g/container/gset/gset_string_set.go +++ b/g/container/gset/gset_string_set.go @@ -120,19 +120,17 @@ func (set *StringSet) String() string { } // LockFunc locks writing with callback function . -func (set *StringSet) LockFunc(f func(m map[string]struct{})) *StringSet { +func (set *StringSet) LockFunc(f func(m map[string]struct{})) { set.mu.Lock() defer set.mu.Unlock() f(set.m) - return set } // RLockFunc locks reading with callback function . -func (set *StringSet) RLockFunc(f func(m map[string]struct{})) *StringSet { +func (set *StringSet) RLockFunc(f func(m map[string]struct{})) { set.mu.RLock() defer set.mu.RUnlock() f(set.m) - return set } // Equal checks whether the two sets equal. diff --git a/g/container/gmap/gmap_func.go b/g/container/gtree/gtree.go similarity index 59% rename from g/container/gmap/gmap_func.go rename to g/container/gtree/gtree.go index 8a6eea0db..fdb1e9132 100644 --- a/g/container/gmap/gmap_func.go +++ b/g/container/gtree/gtree.go @@ -1,8 +1,8 @@ // Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with gm file, +// If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -package gmap - +// Package gtree provides concurrent-safe/unsafe tree containers. +package gtree diff --git a/g/container/gtree/gtree_avltree.go b/g/container/gtree/gtree_avltree.go new file mode 100644 index 000000000..f6833871e --- /dev/null +++ b/g/container/gtree/gtree_avltree.go @@ -0,0 +1,701 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtree + +import ( + "fmt" + "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/internal/rwmutex" +) + +// AVLTree holds elements of the AVL tree. +type AVLTree struct { + mu *rwmutex.RWMutex + root *AVLTreeNode + comparator func(v1, v2 interface{}) int + size int +} + +// AVLTreeNode is a single element within the tree. +type AVLTreeNode struct { + Key interface{} + Value interface{} + parent *AVLTreeNode + children [2]*AVLTreeNode + b int8 +} + +// NewAVLTree instantiates an AVL tree with the custom comparator. +// The param 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 { + return &AVLTree{ + mu : rwmutex.New(unsafe...), + comparator: comparator, + } +} + +// NewAVLTreeFrom instantiates an AVL tree with the custom comparator and data map. +// The param 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 { + tree := NewAVLTree(comparator, unsafe...) + for k, v := range data { + tree.put(k, v, nil, &tree.root) + } + return tree +} + +// Clone returns a new tree with a copy of current tree. +func (tree *AVLTree) Clone(unsafe ...bool) *AVLTree { + newTree := NewAVLTree(tree.comparator, !tree.mu.IsSafe()) + newTree.Sets(tree.Map()) + return newTree +} + +// Set inserts node into the tree. +func (tree *AVLTree) Set(key interface{}, value interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + tree.put(key, value, nil, &tree.root) +} + +// Sets batch sets key-values to the tree. +func (tree *AVLTree) Sets(data map[interface{}]interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + for key, value := range data { + tree.put(key, value, nil, &tree.root) + } +} + +// Search searches the tree with given . +// Second return parameter is true if key was found, otherwise false. +func (tree *AVLTree) Search(key interface{}) (value interface{}, found bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + return tree.doSearch(key) +} + +// doSearch searches the tree with given . +// Second return parameter is true if key was found, otherwise false. +func (tree *AVLTree) doSearch(key interface{}) (value interface{}, found bool) { + n := tree.root + 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] + } + } + return nil, false +} + +// Get searches the node in the tree by and returns its value or nil if key is not found in tree. +func (tree *AVLTree) Get(key interface{}) (value interface{}) { + value, _ = tree.Search(key) + return +} + +// doSetWithLockCheck checks whether value of the key exists with mutex.Lock, +// if not exists, set value to the map with given , +// or else just return the existing value. +// +// When setting value, if is type of , +// it will be executed with mutex.Lock of the hash map, +// and its return value will be set to the map with . +// +// It returns value with given . +func (tree *AVLTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} { + tree.mu.Lock() + defer tree.mu.Unlock() + if v, ok := tree.doSearch(key); ok { + return v + } + if f, ok := value.(func() interface {}); ok { + value = f() + } + tree.put(key, value, nil, &tree.root) + return value +} + +// GetOrSet returns the value by key, +// or set value with given if not exist and returns this value. +func (tree *AVLTree) GetOrSet(key interface{}, value interface{}) interface{} { + if v, ok := tree.Search(key); !ok { + return tree.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +func (tree *AVLTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} { + if v, ok := tree.Search(key); !ok { + return tree.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// with mutex.Lock of the hash map. +func (tree *AVLTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} { + if v, ok := tree.Search(key); !ok { + return tree.doSetWithLockCheck(key, f) + } else { + return v + } +} + +// GetVar returns a gvar.Var with the value by given . +// The returned gvar.Var is un-concurrent safe. +func (tree *AVLTree) GetVar(key interface{}) *gvar.Var { + return gvar.New(tree.Get(key), true) +} + +// GetVarOrSet returns a gvar.Var with result from GetVarOrSet. +// The returned gvar.Var is un-concurrent safe. +func (tree *AVLTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var { + return gvar.New(tree.GetOrSet(key, value), true) +} + +// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc. +// The returned gvar.Var is un-concurrent safe. +func (tree *AVLTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(tree.GetOrSetFunc(key, f), true) +} + +// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock. +// The returned gvar.Var is un-concurrent safe. +func (tree *AVLTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(tree.GetOrSetFuncLock(key, f), true) +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (tree *AVLTree) SetIfNotExist(key interface{}, value interface{}) bool { + if !tree.Contains(key) { + tree.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (tree *AVLTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool { + if !tree.Contains(key) { + tree.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (tree *AVLTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool { + if !tree.Contains(key) { + tree.doSetWithLockCheck(key, f) + return true + } + return false +} + +// Contains checks whether exists in the tree. +func (tree *AVLTree) Contains(key interface{}) bool { + _, ok := tree.Search(key) + return ok +} + +// Remove remove the node from the tree by key. +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *AVLTree) Remove(key interface{}) (value interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + value, _ = tree.remove(key, &tree.root) + return +} + +// Removes batch deletes values of the tree by . +func (tree *AVLTree) Removes(keys []interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + for _, key := range keys { + tree.remove(key, &tree.root) + } +} + +// IsEmpty returns true if tree does not contain any nodes. +func (tree *AVLTree) IsEmpty() bool { + return tree.Size() == 0 +} + +// Size returns number of nodes in the tree. +func (tree *AVLTree) Size() int { + tree.mu.RLock() + defer tree.mu.RUnlock() + return tree.size +} + +// Keys returns all keys in asc order. +func (tree *AVLTree) Keys() []interface{} { + keys := make([]interface{}, tree.Size()) + index := 0 + tree.IteratorAsc(func(key, value interface{}) bool { + keys[index] = key + index++ + return true + }) + return keys +} + +// Values returns all values in asc order based on the key. +func (tree *AVLTree) Values() []interface{} { + values := make([]interface{}, tree.Size()) + index := 0 + tree.IteratorAsc(func(key, value interface{}) bool { + values[index] = value + index++ + return true + }) + return values +} + +// Left returns the minimum element of the AVL tree +// or nil if the tree is empty. +func (tree *AVLTree) Left() *AVLTreeNode { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.bottom(0) + if tree.mu.IsSafe() { + return &AVLTreeNode { + Key : node.Key, + Value : node.Value, + } + } + return node +} + +// Right returns the maximum element of the AVL tree +// or nil if the tree is empty. +func (tree *AVLTree) Right() *AVLTreeNode { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.bottom(1) + if tree.mu.IsSafe() { + return &AVLTreeNode { + Key : node.Key, + Value : node.Value, + } + } + return node +} + +// Floor Finds floor node of the input key, return the floor node or nil if no ceiling is found. +// Second return parameter is true if floor was found, otherwise false. +// +// Floor node is defined as the largest node that is smaller than or equal to the given node. +// A floor node may not be found, either because the tree is empty, or because +// all nodes in the tree is larger than the given node. +// +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *AVLTree) Floor(key interface{}) (floor *AVLTreeNode, found bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + found = false + n := tree.root + 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] + } + } + if found { + return + } + return nil, false +} + +// Ceiling finds ceiling node of the input key, return the ceiling node or nil if no ceiling is found. +// Second return parameter is true if ceiling was found, otherwise false. +// +// Ceiling node is defined as the smallest node that is larger than or equal to the given node. +// A ceiling node may not be found, either because the tree is empty, or because +// all nodes in the tree is smaller than the given node. +// +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *AVLTree) Ceiling(key interface{}) (floor *AVLTreeNode, found bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + found = false + n := tree.root + 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: + floor, found = n, true + n = n.children[0] + } + } + if found { + return + } + return nil, false +} + +// Clear removes all nodes from the tree. +func (tree *AVLTree) Clear() { + tree.mu.Lock() + defer tree.mu.Unlock() + tree.root = nil + tree.size = 0 +} + +// String returns a string representation of container +func (tree *AVLTree) String() string { + tree.mu.RLock() + defer tree.mu.RUnlock() + str := "AVLTree\n" + if tree.size != 0 { + output(tree.root, "", true, &str) + } + return str +} + +// Print prints the tree to stdout. +func (tree *AVLTree) Print() { + fmt.Println(tree.String()) +} + +// Map returns all key-value items as map. +func (tree *AVLTree) Map() map[interface{}]interface{} { + m := make(map[interface{}]interface{}, tree.Size()) + tree.IteratorAsc(func(key, value interface{}) bool { + m[key] = value + return true + }) + return m +} + +// Flip exchanges key-value of the tree to value-key. +// Note that you should guarantee the value is the same type as key, +// or else the comparator would panic. +// +// If the type of value is different with key, you pass the new . +func (tree *AVLTree) Flip(comparator...func(v1, v2 interface{}) int) { + t := (*AVLTree)(nil) + if len(comparator) > 0 { + t = NewAVLTree(comparator[0], !tree.mu.IsSafe()) + } else { + t = NewAVLTree(tree.comparator, !tree.mu.IsSafe()) + } + tree.IteratorAsc(func(key, value interface{}) bool { + t.put(value, key, nil, &t.root) + return true + }) + tree.mu.Lock() + tree.root = t.root + tree.size = t.size + tree.mu.Unlock() +} + +// Iterator is alias of IteratorAsc. +func (tree *AVLTree) Iterator(f func (key, value interface{}) bool) { + tree.IteratorAsc(f) +} + +// IteratorAsc iterates the tree in ascending order with given callback function . +// If returns true, then it continues iterating; or false to stop. +func (tree *AVLTree) IteratorAsc(f func (key, value interface{}) bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.bottom(0) + for node != nil { + if !f(node.Key, node.Value) { + return + } + node = node.Next() + } +} + +// IteratorDesc iterates the tree in descending order with given callback function . +// If returns true, then it continues iterating; or false to stop. +func (tree *AVLTree) IteratorDesc(f func (key, value interface{}) bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.bottom(1) + for node != nil { + if !f(node.Key, node.Value) { + return + } + node = node.Prev() + } +} + +func (tree *AVLTree) put(key interface{}, value interface{}, p *AVLTreeNode, qp **AVLTreeNode) bool { + q := *qp + if q == nil { + tree.size++ + *qp = &AVLTreeNode{Key: key, Value: value, parent: p} + return true + } + + c := tree.comparator(key, q.Key) + if c == 0 { + q.Key = key + q.Value = value + return false + } + + if c < 0 { + c = -1 + } else { + c = 1 + } + a := (c + 1) / 2 + if tree.put(key, value, q, &q.children[a]) { + return putFix(int8(c), qp) + } + return false +} + +func (tree *AVLTree) remove(key interface{}, qp **AVLTreeNode) (value interface{}, fix bool) { + q := *qp + if q == nil { + return nil, false + } + + c := tree.comparator(key, q.Key) + if c == 0 { + tree.size-- + value = q.Value + fix = true + if q.children[1] == nil { + if q.children[0] != nil { + q.children[0].parent = q.parent + } + *qp = q.children[0] + return + } + if removeMin(&q.children[1], &q.Key, &q.Value) { + return value, removeFix(-1, qp) + } + return + } + + if c < 0 { + c = -1 + } else { + c = 1 + } + a := (c + 1) / 2 + value, fix = tree.remove(key, &q.children[a]) + if fix { + return value, removeFix(int8(-c), qp) + } + return nil, false +} + +func removeMin(qp **AVLTreeNode, minKey *interface{}, minVal *interface{}) bool { + q := *qp + if q.children[0] == nil { + *minKey = q.Key + *minVal = q.Value + if q.children[1] != nil { + q.children[1].parent = q.parent + } + *qp = q.children[1] + return true + } + fix := removeMin(&q.children[0], minKey, minVal) + if fix { + return removeFix(1, qp) + } + return false +} + +func putFix(c int8, t **AVLTreeNode) bool { + s := *t + if s.b == 0 { + s.b = c + return true + } + + if s.b == -c { + s.b = 0 + return false + } + + if s.children[(c+1)/2].b == c { + s = singleRotate(c, s) + } else { + s = doubleRotate(c, s) + } + *t = s + return false +} + +func removeFix(c int8, t **AVLTreeNode) bool { + s := *t + if s.b == 0 { + s.b = c + return false + } + + if s.b == -c { + s.b = 0 + return true + } + + a := (c + 1) / 2 + if s.children[a].b == 0 { + s = rotate(c, s) + s.b = -c + *t = s + return false + } + + if s.children[a].b == c { + s = singleRotate(c, s) + } else { + s = doubleRotate(c, s) + } + *t = s + return true +} + +func singleRotate(c int8, s *AVLTreeNode) *AVLTreeNode { + s.b = 0 + s = rotate(c, s) + s.b = 0 + return s +} + +func doubleRotate(c int8, s *AVLTreeNode) *AVLTreeNode { + a := (c + 1) / 2 + r := s.children[a] + s.children[a] = rotate(-c, s.children[a]) + 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 + } + + p.b = 0 + return p +} + +func rotate(c int8, s *AVLTreeNode) *AVLTreeNode { + a := (c + 1) / 2 + r := s.children[a] + s.children[a] = r.children[a^1] + if s.children[a] != nil { + s.children[a].parent = s + } + r.children[a^1] = s + r.parent = s.parent + s.parent = r + return r +} + +func (tree *AVLTree) bottom(d int) *AVLTreeNode { + n := tree.root + if n == nil { + return nil + } + + for c := n.children[d]; c != nil; c = n.children[d] { + n = c + } + return n +} + +// Prev returns the previous element in an inorder +// walk of the AVL tree. +func (node *AVLTreeNode) Prev() *AVLTreeNode { + return node.walk1(0) +} + +// Next returns the next element in an inorder +// walk of the AVL tree. +func (node *AVLTreeNode) Next() *AVLTreeNode { + return node.walk1(1) +} + +func (node *AVLTreeNode) walk1(a int) *AVLTreeNode { + if node == nil { + return nil + } + n := node + if n.children[a] != nil { + n = n.children[a] + for n.children[a^1] != nil { + n = n.children[a^1] + } + return n + } + + p := n.parent + for p != nil && p.children[a] == n { + n = p + p = p.parent + } + return p +} + +func output(node *AVLTreeNode, prefix string, isTail bool, str *string) { + if node.children[1] != nil { + newPrefix := prefix + if isTail { + newPrefix += "│ " + } else { + newPrefix += " " + } + output(node.children[1], newPrefix, false, str) + } + *str += prefix + if isTail { + *str += "└── " + } else { + *str += "┌── " + } + *str += fmt.Sprintf("%v\n", node.Key) + if node.children[0] != nil { + newPrefix := prefix + if isTail { + newPrefix += " " + } else { + newPrefix += "│ " + } + output(node.children[0], newPrefix, true, str) + } +} \ No newline at end of file diff --git a/g/container/gtree/gtree_btree.go b/g/container/gtree/gtree_btree.go new file mode 100644 index 000000000..57fd061f3 --- /dev/null +++ b/g/container/gtree/gtree_btree.go @@ -0,0 +1,844 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtree + +import ( + "bytes" + "fmt" + "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/internal/rwmutex" + "strings" +) + +// BTree holds elements of the B-tree. +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) +} + +// BTreeNode is a single element within the tree. +type BTreeNode struct { + Parent *BTreeNode + Entries []*BTreeEntry // Contained keys in node + Children []*BTreeNode // Children nodes +} + +// BTreeEntry represents the key-value pair contained within nodes. +type BTreeEntry struct { + Key interface{} + Value interface{} +} + +// NewBTree instantiates a B-tree with (maximum number of children) and a custom key comparator. +// The param used to specify whether using tree in un-concurrent-safety, +// which is false in default. +// Note that the must be greater or equal than 3, or else it panics. +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, + } +} + +// NewBTreeFrom instantiates a B-tree with (maximum number of children), a custom key comparator and data map. +// The param 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 { + tree := NewBTree(m, comparator, unsafe...) + for k, v := range data { + tree.doSet(k, v) + } + return tree +} + +// Clone returns a new tree with a copy of current tree. +func (tree *BTree) Clone(unsafe ...bool) *BTree { + newTree := NewBTree(tree.m, tree.comparator, !tree.mu.IsSafe()) + newTree.Sets(tree.Map()) + return newTree +} + +// Set inserts key-value item into the tree. +func (tree *BTree) Set(key interface{}, value interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + tree.doSet(key, value) +} + +// doSet inserts key-value pair node into the tree. +// If key already exists, then its value is updated with the new value. +func (tree *BTree) doSet(key interface{}, value interface{}) { + entry := &BTreeEntry{Key: key, Value: value} + if tree.root == nil { + tree.root = &BTreeNode{Entries: []*BTreeEntry{entry}, Children: []*BTreeNode{}} + tree.size++ + return + } + + if tree.insert(tree.root, entry) { + tree.size++ + } +} + +// Sets batch sets key-values to the tree. +func (tree *BTree) Sets(data map[interface{}]interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + for k, v := range data { + tree.doSet(k, v) + } +} + +// Get searches the node in the tree by and returns its value or nil if key is not found in tree. +func (tree *BTree) Get(key interface{}) (value interface{}) { + value, _ = tree.Search(key) + return +} + +// doSetWithLockCheck checks whether value of the key exists with mutex.Lock, +// if not exists, set value to the map with given , +// or else just return the existing value. +// +// When setting value, if is type of , +// it will be executed with mutex.Lock of the hash map, +// and its return value will be set to the map with . +// +// It returns value with given . +func (tree *BTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} { + tree.mu.Lock() + defer tree.mu.Unlock() + if entry := tree.doSearch(key); entry != nil { + return entry.Value + } + if f, ok := value.(func() interface {}); ok { + value = f() + } + tree.doSet(key, value) + return value +} + +// GetOrSet returns the value by key, +// or set value with given if not exist and returns this value. +func (tree *BTree) GetOrSet(key interface{}, value interface{}) interface{} { + if v, ok := tree.Search(key); !ok { + return tree.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +func (tree *BTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} { + if v, ok := tree.Search(key); !ok { + return tree.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// with mutex.Lock of the hash map. +func (tree *BTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} { + if v, ok := tree.Search(key); !ok { + return tree.doSetWithLockCheck(key, f) + } else { + return v + } +} + +// GetVar returns a gvar.Var with the value by given . +// The returned gvar.Var is un-concurrent safe. +func (tree *BTree) GetVar(key interface{}) *gvar.Var { + return gvar.New(tree.Get(key), true) +} + +// GetVarOrSet returns a gvar.Var with result from GetVarOrSet. +// The returned gvar.Var is un-concurrent safe. +func (tree *BTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var { + return gvar.New(tree.GetOrSet(key, value), true) +} + +// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc. +// The returned gvar.Var is un-concurrent safe. +func (tree *BTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(tree.GetOrSetFunc(key, f), true) +} + +// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock. +// The returned gvar.Var is un-concurrent safe. +func (tree *BTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(tree.GetOrSetFuncLock(key, f), true) +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (tree *BTree) SetIfNotExist(key interface{}, value interface{}) bool { + if !tree.Contains(key) { + tree.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (tree *BTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool { + if !tree.Contains(key) { + tree.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (tree *BTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool { + if !tree.Contains(key) { + tree.doSetWithLockCheck(key, f) + return true + } + return false +} + +// Contains checks whether exists in the tree. +func (tree *BTree) Contains(key interface{}) bool { + _, ok := tree.Search(key) + return ok +} + +// Remove remove the node from the tree by key. +// Key should adhere to the comparator's type assertion, otherwise method panics. +func (tree *BTree) doRemove(key interface{}) (value interface{}) { + node, index, found := tree.searchRecursively(tree.root, key) + if found { + value = node.Entries[index].Value + tree.delete(node, index) + tree.size-- + } + return +} + +// Remove removes the node from the tree by . +func (tree *BTree) Remove(key interface{}) (value interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + return tree.doRemove(key) +} + +// Removes batch deletes values of the tree by . +func (tree *BTree) Removes(keys []interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + for _, key := range keys { + tree.doRemove(key) + } +} + +// Empty returns true if tree does not contain any nodes +func (tree *BTree) IsEmpty() bool { + return tree.Size() == 0 +} + +// Size returns number of nodes in the tree. +func (tree *BTree) Size() int { + tree.mu.RLock() + defer tree.mu.RUnlock() + return tree.size +} + +// Keys returns all keys in asc order. +func (tree *BTree) Keys() []interface{} { + keys := make([]interface{}, tree.Size()) + index := 0 + tree.IteratorAsc(func(key, value interface{}) bool { + keys[index] = key + index++ + return true + }) + return keys +} + +// Values returns all values in asc order based on the key. +func (tree *BTree) Values() []interface{} { + values := make([]interface{}, tree.Size()) + index := 0 + tree.IteratorAsc(func(key, value interface{}) bool { + values[index] = value + index++ + return true + }) + return values +} + +// Map returns all key-value items as map. +func (tree *BTree) Map() map[interface{}]interface{} { + m := make(map[interface{}]interface{}, tree.Size()) + tree.IteratorAsc(func(key, value interface{}) bool { + m[key] = value + return true + }) + return m +} + +// Clear removes all nodes from the tree. +func (tree *BTree) Clear() { + tree.mu.Lock() + defer tree.mu.Unlock() + tree.root = nil + tree.size = 0 +} + +// Height returns the height of the tree. +func (tree *BTree) Height() int { + tree.mu.RLock() + defer tree.mu.RUnlock() + return tree.root.height() +} + +// Left returns the left-most (min) entry or nil if tree is empty. +func (tree *BTree) Left() *BTreeEntry { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.left(tree.root) + return node.Entries[0] +} + +// Right returns the right-most (max) entry or nil if tree is empty. +func (tree *BTree) Right() *BTreeEntry { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.right(tree.root) + return node.Entries[len(node.Entries) - 1] +} + +// String returns a string representation of container (for debugging purposes) +func (tree *BTree) String() string { + tree.mu.RLock() + defer tree.mu.RUnlock() + var buffer bytes.Buffer + if _, err := buffer.WriteString("BTree\n"); err != nil { + } + if tree.size != 0 { + tree.output(&buffer, tree.root, 0, true) + } + return buffer.String() +} + +// Search searches the tree with given . +// Second return parameter is true if key was found, otherwise false. +func (tree *BTree) Search(key interface{}) (value interface{}, found bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + node, index, found := tree.searchRecursively(tree.root, key) + if found { + return node.Entries[index].Value, true + } + return nil, false +} + +// Search searches the tree with given without mutex. +// It returns the entry if found or otherwise nil. +func (tree *BTree) doSearch(key interface{}) *BTreeEntry { + node, index, found := tree.searchRecursively(tree.root, key) + if found { + return node.Entries[index] + } + return nil +} + +// Print prints the tree to stdout. +func (tree *BTree) Print() { + fmt.Println(tree.String()) +} + +// Iterator is alias of IteratorAsc. +func (tree *BTree) Iterator(f func (key, value interface{}) bool) { + tree.IteratorAsc(f) +} + +// IteratorAsc iterates the tree in ascending order with given callback function . +// If returns true, then it continues iterating; or false to stop. +func (tree *BTree) IteratorAsc(f func (key, value interface{}) bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.left(tree.root) + if node == nil { + return + } + entry := node.Entries[0] +loop: + if entry == nil { + return + } + if !f(entry.Key, entry.Value) { + return + } + // 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] + // Try to go down to the child left of the current node + for len(node.Children) > 0 { + node = node.Children[0] + } + // Return the left-most entry + entry = node.Entries[0] + 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] + goto loop + } + // Reached leaf node and there are no entries to the right of the current entry, so go up to the parent + for node.Parent != nil { + node = node.Parent + // Find next 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 next entry position in current node + if e < len(node.Entries) { + entry = node.Entries[e] + goto loop + } + } +} + +// IteratorDesc iterates the tree in descending order with given callback function . +// If returns true, then it continues iterating; or false to stop. +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] +loop: + if entry == nil { + return + } + if !f(entry.Key, entry.Value) { + return + } + // Find current entry position in current node + e, _ := tree.search(node, entry.Key) + // Try to go down to the child left of the current entry + if e < len(node.Children) { + 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] + } + // Return the right-most entry + 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] + goto loop + } + + // Reached leaf node and there are no entries to the left of the current entry, so go up to the parent + for node.Parent != nil { + node = node.Parent + // 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] + goto loop + } + } +} + +func (tree *BTree) output(buffer *bytes.Buffer, node *BTreeNode, level int, isTail bool) { + for e := 0; e < len(node.Entries)+1; e++ { + if e < len(node.Children) { + tree.output(buffer, node.Children[e], level+1, true) + } + if e < len(node.Entries) { + if _, err := buffer.WriteString(strings.Repeat(" ", level)); err != nil { + } + if _, err := buffer.WriteString(fmt.Sprintf("%v", node.Entries[e].Key) + "\n"); err != nil { + } + } + } +} + +func (node *BTreeNode) height() int { + h := 0 + n := node + for ; n != nil; n = n.Children[0] { + h++ + if len(n.Children) == 0 { + break + } + } + return h +} + +func (tree *BTree) isLeaf(node *BTreeNode) bool { + return len(node.Children) == 0 +} + +func (tree *BTree) isFull(node *BTreeNode) bool { + return len(node.Entries) == tree.maxEntries() +} + +func (tree *BTree) shouldSplit(node *BTreeNode) bool { + return len(node.Entries) > tree.maxEntries() +} + +func (tree *BTree) maxChildren() int { + return tree.m +} + +func (tree *BTree) minChildren() int { + return (tree.m + 1) / 2 // ceil(m/2) +} + +func (tree *BTree) maxEntries() int { + return tree.maxChildren() - 1 +} + +func (tree *BTree) minEntries() int { + return tree.minChildren() - 1 +} + +func (tree *BTree) middle() int { + // "-1" to favor right nodes to have more keys when splitting + return (tree.m - 1) / 2 +} + +// 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 + 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 + } + } + return low, false +} + +// searchRecursively searches recursively down the tree starting at the startNode +func (tree *BTree) searchRecursively(startNode *BTreeNode, key interface{}) (node *BTreeNode, index int, found bool) { + if tree.size == 0 { + return nil, -1, false + } + node = startNode + for { + index, found = tree.search(node, key) + if found { + return node, index, true + } + if tree.isLeaf(node) { + return nil, -1, false + } + node = node.Children[index] + } +} + +func (tree *BTree) insert(node *BTreeNode, entry *BTreeEntry) (inserted bool) { + if tree.isLeaf(node) { + return tree.insertIntoLeaf(node, entry) + } + return tree.insertIntoInternal(node, entry) +} + +func (tree *BTree) insertIntoLeaf(node *BTreeNode, entry *BTreeEntry) (inserted bool) { + insertPosition, found := tree.search(node, entry.Key) + if found { + node.Entries[insertPosition] = entry + return false + } + // Insert entry's key in the middle of the node + node.Entries = append(node.Entries, nil) + copy(node.Entries[insertPosition+1:], node.Entries[insertPosition:]) + node.Entries[insertPosition] = entry + tree.split(node) + return true +} + +func (tree *BTree) insertIntoInternal(node *BTreeNode, entry *BTreeEntry) (inserted bool) { + insertPosition, found := tree.search(node, entry.Key) + if found { + node.Entries[insertPosition] = entry + return false + } + return tree.insert(node.Children[insertPosition], entry) +} + +func (tree *BTree) split(node *BTreeNode) { + if !tree.shouldSplit(node) { + return + } + + if node == tree.root { + tree.splitRoot() + return + } + + tree.splitNonRoot(node) +} + +func (tree *BTree) splitNonRoot(node *BTreeNode) { + middle := tree.middle() + parent := node.Parent + + left := &BTreeNode{Entries: append([]*BTreeEntry(nil), node.Entries[:middle]...), Parent: parent} + right := &BTreeNode{Entries: append([]*BTreeEntry(nil), node.Entries[middle+1:]...), Parent: parent} + + // Move children from the node to be split into left and right nodes + if !tree.isLeaf(node) { + left.Children = append([]*BTreeNode(nil), node.Children[:middle+1]...) + right.Children = append([]*BTreeNode(nil), node.Children[middle+1:]...) + setParent(left.Children, left) + setParent(right.Children, right) + } + + insertPosition, _ := tree.search(parent, node.Entries[middle].Key) + + // Insert middle key into parent + parent.Entries = append(parent.Entries, nil) + copy(parent.Entries[insertPosition+1:], parent.Entries[insertPosition:]) + parent.Entries[insertPosition] = node.Entries[middle] + + // Set child left of inserted key in parent to the created left node + parent.Children[insertPosition] = left + + // Set child right of inserted key in parent to the created right node + parent.Children = append(parent.Children, nil) + copy(parent.Children[insertPosition+2:], parent.Children[insertPosition+1:]) + parent.Children[insertPosition+1] = right + + tree.split(parent) +} + +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:]...)} + + // Move children from the node to be split into left and right nodes + if !tree.isLeaf(tree.root) { + left.Children = append([]*BTreeNode(nil), tree.root.Children[:middle+1]...) + right.Children = append([]*BTreeNode(nil), tree.root.Children[middle+1:]...) + setParent(left.Children, left) + setParent(right.Children, right) + } + + // Root is a node with one entry and two children (left and right) + newRoot := &BTreeNode{ + Entries: []*BTreeEntry{tree.root.Entries[middle]}, + Children: []*BTreeNode{left, right}, + } + + left.Parent = newRoot + right.Parent = newRoot + tree.root = newRoot +} + +func setParent(nodes []*BTreeNode, parent *BTreeNode) { + for _, node := range nodes { + node.Parent = parent + } +} + +func (tree *BTree) left(node *BTreeNode) *BTreeNode { + if tree.size == 0 { + return nil + } + current := node + for { + if tree.isLeaf(current) { + return current + } + current = current.Children[0] + } +} + +func (tree *BTree) right(node *BTreeNode) *BTreeNode { + if tree.size == 0 { + return nil + } + current := node + for { + if tree.isLeaf(current) { + return current + } + current = current.Children[len(current.Children)-1] + } +} + +// leftSibling returns the node's left sibling and child index (in parent) if it exists, otherwise (nil,-1) +// key is any of keys in node (could even be deleted). +func (tree *BTree) leftSibling(node *BTreeNode, key interface{}) (*BTreeNode, int) { + if node.Parent != nil { + index, _ := tree.search(node.Parent, key) + index-- + if index >= 0 && index < len(node.Parent.Children) { + return node.Parent.Children[index], index + } + } + return nil, -1 +} + +// rightSibling returns the node's right sibling and child index (in parent) if it exists, otherwise (nil,-1) +// key is any of keys in node (could even be deleted). +func (tree *BTree) rightSibling(node *BTreeNode, key interface{}) (*BTreeNode, int) { + if node.Parent != nil { + index, _ := tree.search(node.Parent, key) + index++ + if index < len(node.Parent.Children) { + return node.Parent.Children[index], index + } + } + return nil, -1 +} + +// delete deletes an entry in node at entries' index +// ref.: https://en.wikipedia.org/wiki/B-tree#Deletion +func (tree *BTree) delete(node *BTreeNode, index int) { + // deleting from a leaf node + if tree.isLeaf(node) { + deletedKey := node.Entries[index].Key + tree.deleteEntry(node, index) + tree.rebalance(node, deletedKey) + if len(tree.root.Entries) == 0 { + tree.root = nil + } + return + } + + // deleting from an internal node + 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 + tree.deleteEntry(leftLargestNode, leftLargestEntryIndex) + tree.rebalance(leftLargestNode, deletedKey) +} + +// rebalance rebalances the tree after deletion if necessary and returns true, otherwise false. +// Note that we first delete the entry and then call rebalance, thus the passed deleted key as reference. +func (tree *BTree) rebalance(node *BTreeNode, deletedKey interface{}) { + // check if rebalancing is needed + if node == nil || len(node.Entries) >= tree.minEntries() { + return + } + + // try to borrow from left sibling + leftSibling, leftSiblingIndex := tree.leftSibling(node, deletedKey) + if leftSibling != nil && len(leftSibling.Entries) > tree.minEntries() { + // rotate right + node.Entries = append([]*BTreeEntry{node.Parent.Entries[leftSiblingIndex]}, node.Entries...) // prepend parent's separator entry to node's entries + node.Parent.Entries[leftSiblingIndex] = leftSibling.Entries[len(leftSibling.Entries)-1] + tree.deleteEntry(leftSibling, len(leftSibling.Entries)-1) + if !tree.isLeaf(leftSibling) { + leftSiblingRightMostChild := leftSibling.Children[len(leftSibling.Children)-1] + leftSiblingRightMostChild.Parent = node + node.Children = append([]*BTreeNode{leftSiblingRightMostChild}, node.Children...) + tree.deleteChild(leftSibling, len(leftSibling.Children)-1) + } + return + } + + // try to borrow from right sibling + rightSibling, rightSiblingIndex := tree.rightSibling(node, deletedKey) + if rightSibling != nil && len(rightSibling.Entries) > tree.minEntries() { + // rotate left + node.Entries = append(node.Entries, node.Parent.Entries[rightSiblingIndex-1]) // append parent's separator entry to node's entries + node.Parent.Entries[rightSiblingIndex-1] = rightSibling.Entries[0] + tree.deleteEntry(rightSibling, 0) + if !tree.isLeaf(rightSibling) { + rightSiblingLeftMostChild := rightSibling.Children[0] + rightSiblingLeftMostChild.Parent = node + node.Children = append(node.Children, rightSiblingLeftMostChild) + tree.deleteChild(rightSibling, 0) + } + return + } + + // merge with siblings + if rightSibling != nil { + // 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 + 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]) + node.Entries = append(entries, node.Entries...) + deletedKey = node.Parent.Entries[leftSiblingIndex].Key + tree.deleteEntry(node.Parent, leftSiblingIndex) + tree.prependChildren(node.Parent.Children[leftSiblingIndex], node) + tree.deleteChild(node.Parent, leftSiblingIndex) + } + + // make the merged node the root if its parent was the root and the root is empty + if node.Parent == tree.root && len(tree.root.Entries) == 0 { + tree.root = node + node.Parent = nil + return + } + + // parent might underflow, so try to rebalance if necessary + tree.rebalance(node.Parent, deletedKey) +} + +func (tree *BTree) prependChildren(fromNode *BTreeNode, toNode *BTreeNode) { + children := append([]*BTreeNode(nil), fromNode.Children...) + toNode.Children = append(children, toNode.Children...) + setParent(fromNode.Children, toNode) +} + +func (tree *BTree) appendChildren(fromNode *BTreeNode, toNode *BTreeNode) { + toNode.Children = append(toNode.Children, fromNode.Children...) + setParent(fromNode.Children, toNode) +} + +func (tree *BTree) deleteEntry(node *BTreeNode, index int) { + copy(node.Entries[index:], node.Entries[index+1:]) + node.Entries[len(node.Entries)-1] = nil + node.Entries = node.Entries[:len(node.Entries)-1] +} + +func (tree *BTree) deleteChild(node *BTreeNode, index int) { + if index >= len(node.Children) { + return + } + copy(node.Children[index:], node.Children[index+1:]) + node.Children[len(node.Children)-1] = nil + node.Children = node.Children[:len(node.Children)-1] +} \ No newline at end of file diff --git a/g/container/gtree/gtree_redblacktree.go b/g/container/gtree/gtree_redblacktree.go new file mode 100644 index 000000000..ed8d16bf4 --- /dev/null +++ b/g/container/gtree/gtree_redblacktree.go @@ -0,0 +1,832 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gtree + +import ( + "fmt" + "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/internal/rwmutex" +) + +type color bool + +const ( + black, red color = true, false +) + +// RedBlackTree holds elements of the red-black tree. +type RedBlackTree struct { + mu *rwmutex.RWMutex + root *RedBlackTreeNode + size int + comparator func(v1, v2 interface{}) int +} + +// RedBlackTreeNode is a single element within the tree. +type RedBlackTreeNode struct { + Key interface{} + Value interface{} + color color + left *RedBlackTreeNode + right *RedBlackTreeNode + parent *RedBlackTreeNode +} + +// NewRedBlackTree instantiates a red-black tree with the custom comparator. +// The param 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...), + comparator: comparator, + } +} + +// NewRedBlackTreeFrom instantiates a red-black tree with the custom comparator and map. +// The param 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 { + tree := NewRedBlackTree(comparator, unsafe...) + for k, v := range data { + tree.doSet(k, v) + } + return tree +} + +// Clone returns a new tree with a copy of current tree. +func (tree *RedBlackTree) Clone(unsafe ...bool) *RedBlackTree { + newTree := NewRedBlackTree(tree.comparator, !tree.mu.IsSafe()) + newTree.Sets(tree.Map()) + return newTree +} + +// Set inserts key-value item into the tree. +func (tree *RedBlackTree) Set(key interface{}, value interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + tree.doSet(key, value) +} + +// Sets batch sets key-values to the tree. +func (tree *RedBlackTree) Sets(data map[interface{}]interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + for k, v := range data { + tree.doSet(k, v) + } +} + +// doSet inserts key-value item into the tree without mutex. +func (tree *RedBlackTree) doSet(key interface{}, value interface{}) { + insertedNode := (*RedBlackTreeNode)(nil) + 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} + insertedNode = tree.root + } else { + node := tree.root + loop := true + 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 + } + } + } + insertedNode.parent = node + } + tree.insertCase1(insertedNode) + tree.size++ +} + +// Get searches the node in the tree by and returns its value or nil if key is not found in tree. +func (tree *RedBlackTree) Get(key interface{}) (value interface{}) { + value, _ = tree.Search(key) + return +} + +// doSetWithLockCheck checks whether value of the key exists with mutex.Lock, +// if not exists, set value to the map with given , +// or else just return the existing value. +// +// When setting value, if is type of , +// it will be executed with mutex.Lock of the hash map, +// and its return value will be set to the map with . +// +// It returns value with given . +func (tree *RedBlackTree) doSetWithLockCheck(key interface{}, value interface{}) interface{} { + tree.mu.Lock() + defer tree.mu.Unlock() + if node := tree.doSearch(key); node != nil { + return node.Value + } + if f, ok := value.(func() interface {}); ok { + value = f() + } + tree.doSet(key, value) + return value +} + +// GetOrSet returns the value by key, +// or set value with given if not exist and returns this value. +func (tree *RedBlackTree) GetOrSet(key interface{}, value interface{}) interface{} { + if v, ok := tree.Search(key); !ok { + return tree.doSetWithLockCheck(key, value) + } else { + return v + } +} + +// GetOrSetFunc returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +func (tree *RedBlackTree) GetOrSetFunc(key interface{}, f func() interface{}) interface{} { + if v, ok := tree.Search(key); !ok { + return tree.doSetWithLockCheck(key, f()) + } else { + return v + } +} + +// GetOrSetFuncLock returns the value by key, +// or sets value with return value of callback function if not exist +// and returns this value. +// +// GetOrSetFuncLock differs with GetOrSetFunc function is that it executes function +// with mutex.Lock of the hash map. +func (tree *RedBlackTree) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} { + if v, ok := tree.Search(key); !ok { + return tree.doSetWithLockCheck(key, f) + } else { + return v + } +} + +// GetVar returns a gvar.Var with the value by given . +// The returned gvar.Var is un-concurrent safe. +func (tree *RedBlackTree) GetVar(key interface{}) *gvar.Var { + return gvar.New(tree.Get(key), true) +} + +// GetVarOrSet returns a gvar.Var with result from GetVarOrSet. +// The returned gvar.Var is un-concurrent safe. +func (tree *RedBlackTree) GetVarOrSet(key interface{}, value interface{}) *gvar.Var { + return gvar.New(tree.GetOrSet(key, value), true) +} + +// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc. +// The returned gvar.Var is un-concurrent safe. +func (tree *RedBlackTree) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(tree.GetOrSetFunc(key, f), true) +} + +// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock. +// The returned gvar.Var is un-concurrent safe. +func (tree *RedBlackTree) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var { + return gvar.New(tree.GetOrSetFuncLock(key, f), true) +} + +// SetIfNotExist sets to the map if the does not exist, then return true. +// It returns false if exists, and would be ignored. +func (tree *RedBlackTree) SetIfNotExist(key interface{}, value interface{}) bool { + if !tree.Contains(key) { + tree.doSetWithLockCheck(key, value) + return true + } + return false +} + +// SetIfNotExistFunc sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +func (tree *RedBlackTree) SetIfNotExistFunc(key interface{}, f func() interface{}) bool { + if !tree.Contains(key) { + tree.doSetWithLockCheck(key, f()) + return true + } + return false +} + +// SetIfNotExistFuncLock sets value with return value of callback function , then return true. +// It returns false if exists, and would be ignored. +// +// SetIfNotExistFuncLock differs with SetIfNotExistFunc function is that +// it executes function with mutex.Lock of the hash map. +func (tree *RedBlackTree) SetIfNotExistFuncLock(key interface{}, f func() interface{}) bool { + if !tree.Contains(key) { + tree.doSetWithLockCheck(key, f) + return true + } + return false +} + +// Contains checks whether exists in the tree. +func (tree *RedBlackTree) Contains(key interface{}) bool { + _, ok := tree.Search(key) + return ok +} + +// doRemove removes the node from the tree by without mutex. +func (tree *RedBlackTree) doRemove(key interface{}) (value interface{}) { + child := (*RedBlackTreeNode)(nil) + 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 + node.Value = p.Value + node = p + } + if node.left == nil || node.right == nil { + if node.right == nil { + child = node.left + } else { + child = node.right + } + if node.color == black { + node.color = tree.nodeColor(child) + tree.deleteCase1(node) + } + tree.replaceNode(node, child) + if node.parent == nil && child != nil { + child.color = black + } + } + tree.size-- + return +} + +// Remove removes the node from the tree by . +func (tree *RedBlackTree) Remove(key interface{}) (value interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + return tree.doRemove(key) +} + +// Removes batch deletes values of the tree by . +func (tree *RedBlackTree) Removes(keys []interface{}) { + tree.mu.Lock() + defer tree.mu.Unlock() + for _, key := range keys { + tree.doRemove(key) + } +} + +// IsEmpty returns true if tree does not contain any nodes. +func (tree *RedBlackTree) IsEmpty() bool { + return tree.Size() == 0 +} + +// Size returns number of nodes in the tree. +func (tree *RedBlackTree) Size() int { + tree.mu.RLock() + defer tree.mu.RUnlock() + return tree.size +} + +// Keys returns all keys in asc order. +func (tree *RedBlackTree) Keys() []interface{} { + keys := make([]interface{}, tree.Size()) + index := 0 + tree.IteratorAsc(func(key, value interface{}) bool { + keys[index] = key + index++ + return true + }) + return keys +} + +// Values returns all values in asc order based on the key. +func (tree *RedBlackTree) Values() []interface{} { + values := make([]interface{}, tree.Size()) + index := 0 + tree.IteratorAsc(func(key, value interface{}) bool { + values[index] = value + index++ + return true + }) + return values +} + +// Map returns all key-value items as map. +func (tree *RedBlackTree) Map() map[interface{}]interface{} { + m := make(map[interface{}]interface{}, tree.Size()) + tree.IteratorAsc(func(key, value interface{}) bool { + m[key] = value + return true + }) + return m +} + +// Left returns the left-most (min) node or nil if tree is empty. +func (tree *RedBlackTree) Left() *RedBlackTreeNode { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.leftNode() + if tree.mu.IsSafe() { + return &RedBlackTreeNode{ + Key : node.Key, + Value : node.Value, + } + } + return node +} + +// Right returns the right-most (max) node or nil if tree is empty. +func (tree *RedBlackTree) Right() *RedBlackTreeNode { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.rightNode() + if tree.mu.IsSafe() { + return &RedBlackTreeNode{ + Key : node.Key, + Value : node.Value, + } + } + return node +} + +// leftNode returns the left-most (min) node or nil if tree is empty. +func (tree *RedBlackTree) leftNode() *RedBlackTreeNode { + p := (*RedBlackTreeNode)(nil) + n := tree.root + for n != nil { + p = n + n = n.left + } + return p +} + +// rightNode returns the right-most (max) node or nil if tree is empty. +func (tree *RedBlackTree) rightNode() *RedBlackTreeNode { + p := (*RedBlackTreeNode)(nil) + n := tree.root + for n != nil { + p = n + n = n.right + } + return p +} + +// Floor Finds floor node of the input , return the floor node or nil if no floor is found. +// +// Floor node is defined as the largest node that its key is smaller than or equal to the given . +// A floor node may not be found, either because the tree is empty, or because +// all nodes in the tree are larger than the given node. +func (tree *RedBlackTree) Floor(key interface{}) (floor *RedBlackTreeNode) { + tree.mu.RLock() + defer tree.mu.RUnlock() + found := false + node := tree.root + for node != nil { + compare := tree.comparator(key, node.Key) + switch { + case compare == 0: + return node + case compare < 0: + node = node.left + case compare > 0: + floor, found = node, true + node = node.right + } + } + if found { + return floor + } + return nil +} + +// Ceiling finds ceiling node of the input , return the ceiling node or nil if no ceiling is found. +// +// Ceiling node is defined as the smallest node that its key is larger than or equal to the given . +// A ceiling node may not be found, either because the tree is empty, or because +// all nodes in the tree are smaller than the given node. +func (tree *RedBlackTree) Ceiling(key interface{}) (ceiling *RedBlackTreeNode) { + tree.mu.RLock() + defer tree.mu.RUnlock() + found := false + node := tree.root + for node != nil { + compare := tree.comparator(key, node.Key) + switch { + case compare == 0: + return node + case compare < 0: + ceiling, found = node, true + node = node.left + case compare > 0: + node = node.right + } + } + if found { + return ceiling + } + return nil +} + +// Iterator is alias of IteratorAsc. +func (tree *RedBlackTree) Iterator(f func (key, value interface{}) bool) { + tree.IteratorAsc(f) +} + +// IteratorAsc iterates the tree in ascending order with given callback function . +// If returns true, then it continues iterating; or false to stop. +func (tree *RedBlackTree) IteratorAsc(f func (key, value interface{}) bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.leftNode() + if node == nil { + return + } +loop: + if node == nil { + return + } + if !f(node.Key, node.Value) { + return + } + if node.right != nil { + node = node.right + for node.left != nil { + node = node.left + } + goto loop + } + if node.parent != nil { + old := node + for node.parent != nil { + node = node.parent + if tree.comparator(old.Key, node.Key) <= 0 { + goto loop + } + } + } +} + +// IteratorDesc iterates the tree in descending order with given callback function . +// If returns true, then it continues iterating; or false to stop. +func (tree *RedBlackTree) IteratorDesc(f func (key, value interface{}) bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.rightNode() + if node == nil { + return + } +loop: + if node == nil { + return + } + if !f(node.Key, node.Value) { + return + } + if node.left != nil { + node = node.left + for node.right != nil { + node = node.right + } + goto loop + } + if node.parent != nil { + old := node + for node.parent != nil { + node = node.parent + if tree.comparator(old.Key, node.Key) >= 0 { + goto loop + } + } + } +} + +// Clear removes all nodes from the tree. +func (tree *RedBlackTree) Clear() { + tree.mu.Lock() + defer tree.mu.Unlock() + tree.root = nil + tree.size = 0 +} + +// String returns a string representation of container. +func (tree *RedBlackTree) String() string { + tree.mu.RLock() + defer tree.mu.RUnlock() + str := "RedBlackTree\n" + if tree.size != 0 { + tree.output(tree.root, "", true, &str) + } + return str +} + +// Print prints the tree to stdout. +func (tree *RedBlackTree) Print() { + fmt.Println(tree.String()) +} + +// Search searches the tree with given . +// Second return parameter is true if key was found, otherwise false. +func (tree *RedBlackTree) Search(key interface{}) (value interface{}, found bool) { + tree.mu.RLock() + defer tree.mu.RUnlock() + node := tree.doSearch(key) + if node != nil { + return node.Value, true + } + return nil, false +} + +// Flip exchanges key-value of the tree to value-key. +// Note that you should guarantee the value is the same type as key, +// or else the comparator would panic. +// +// If the type of value is different with key, you pass the new . +func (tree *RedBlackTree) Flip(comparator...func(v1, v2 interface{}) int) { + t := (*RedBlackTree)(nil) + if len(comparator) > 0 { + t = NewRedBlackTree(comparator[0], !tree.mu.IsSafe()) + } else { + t = NewRedBlackTree(tree.comparator, !tree.mu.IsSafe()) + } + tree.IteratorAsc(func(key, value interface{}) bool { + t.doSet(value, key) + return true + }) + tree.mu.Lock() + tree.root = t.root + tree.size = t.size + tree.mu.Unlock() +} + +func (tree *RedBlackTree) output(node *RedBlackTreeNode, prefix string, isTail bool, str *string) { + if node.right != nil { + newPrefix := prefix + if isTail { + newPrefix += "│ " + } else { + newPrefix += " " + } + tree.output(node.right, newPrefix, false, str) + } + *str += prefix + if isTail { + *str += "└── " + } else { + *str += "┌── " + } + *str += fmt.Sprintf("%v\n", node.Key) + if node.left != nil { + newPrefix := prefix + if isTail { + newPrefix += " " + } else { + newPrefix += "│ " + } + tree.output(node.left, newPrefix, true, str) + } +} + +// doSearch searches the tree with given without mutex. +// It returns the node if found or otherwise nil. +func (tree *RedBlackTree) doSearch(key interface{}) *RedBlackTreeNode { + node := tree.root + 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 + } + } + return nil +} + +func (node *RedBlackTreeNode) grandparent() *RedBlackTreeNode { + if node != nil && node.parent != nil { + return node.parent.parent + } + return nil +} + +func (node *RedBlackTreeNode) uncle() *RedBlackTreeNode { + if node == nil || node.parent == nil || node.parent.parent == nil { + return nil + } + return node.parent.sibling() +} + +func (node *RedBlackTreeNode) sibling() *RedBlackTreeNode { + if node == nil || node.parent == nil { + return nil + } + if node == node.parent.left { + return node.parent.right + } + return node.parent.left +} + +func (tree *RedBlackTree) rotateLeft(node *RedBlackTreeNode) { + right := node.right + tree.replaceNode(node, right) + node.right = right.left + if right.left != nil { + right.left.parent = node + } + right.left = node + node.parent = right +} + +func (tree *RedBlackTree) rotateRight(node *RedBlackTreeNode) { + left := node.left + tree.replaceNode(node, left) + node.left = left.right + if left.right != nil { + left.right.parent = node + } + left.right = node + node.parent = left +} + +func (tree *RedBlackTree) replaceNode(old *RedBlackTreeNode, new *RedBlackTreeNode) { + if old.parent == nil { + tree.root = new + } else { + if old == old.parent.left { + old.parent.left = new + } else { + old.parent.right = new + } + } + if new != nil { + new.parent = old.parent + } +} + +func (tree *RedBlackTree) insertCase1(node *RedBlackTreeNode) { + if node.parent == nil { + node.color = black + } else { + tree.insertCase2(node) + } +} + +func (tree *RedBlackTree) insertCase2(node *RedBlackTreeNode) { + if tree.nodeColor(node.parent) == black { + return + } + tree.insertCase3(node) +} + +func (tree *RedBlackTree) insertCase3(node *RedBlackTreeNode) { + uncle := node.uncle() + if tree.nodeColor(uncle) == red { + node.parent.color = black + uncle.color = black + node.grandparent().color = red + tree.insertCase1(node.grandparent()) + } else { + tree.insertCase4(node) + } +} + +func (tree *RedBlackTree) insertCase4(node *RedBlackTreeNode) { + grandparent := node.grandparent() + if node == node.parent.right && node.parent == grandparent.left { + tree.rotateLeft(node.parent) + node = node.left + } else if node == node.parent.left && node.parent == grandparent.right { + tree.rotateRight(node.parent) + node = node.right + } + tree.insertCase5(node) +} + +func (tree *RedBlackTree) insertCase5(node *RedBlackTreeNode) { + node.parent.color = black + grandparent := node.grandparent() + grandparent.color = red + if node == node.parent.left && node.parent == grandparent.left { + tree.rotateRight(grandparent) + } else if node == node.parent.right && node.parent == grandparent.right { + tree.rotateLeft(grandparent) + } +} + +func (node *RedBlackTreeNode) maximumNode() *RedBlackTreeNode { + if node == nil { + return nil + } + for node.right != nil { + return node.right + } + return node +} + +func (tree *RedBlackTree) deleteCase1(node *RedBlackTreeNode) { + if node.parent == nil { + return + } + tree.deleteCase2(node) +} + +func (tree *RedBlackTree) deleteCase2(node *RedBlackTreeNode) { + sibling := node.sibling() + if tree.nodeColor(sibling) == red { + node.parent.color = red + sibling.color = black + if node == node.parent.left { + tree.rotateLeft(node.parent) + } else { + tree.rotateRight(node.parent) + } + } + tree.deleteCase3(node) +} + +func (tree *RedBlackTree) deleteCase3(node *RedBlackTreeNode) { + sibling := node.sibling() + if tree.nodeColor(node.parent) == black && + tree.nodeColor(sibling) == black && + tree.nodeColor(sibling.left) == black && + tree.nodeColor(sibling.right) == black { + sibling.color = red + tree.deleteCase1(node.parent) + } else { + tree.deleteCase4(node) + } +} + +func (tree *RedBlackTree) deleteCase4(node *RedBlackTreeNode) { + sibling := node.sibling() + if tree.nodeColor(node.parent) == red && + tree.nodeColor(sibling) == black && + tree.nodeColor(sibling.left) == black && + tree.nodeColor(sibling.right) == black { + sibling.color = red + node.parent.color = black + } else { + tree.deleteCase5(node) + } +} + +func (tree *RedBlackTree) deleteCase5(node *RedBlackTreeNode) { + sibling := node.sibling() + if node == node.parent.left && + tree.nodeColor(sibling) == black && + tree.nodeColor(sibling.left) == red && + tree.nodeColor(sibling.right) == black { + 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.right.color = black + tree.rotateLeft(sibling) + } + tree.deleteCase6(node) +} + +func (tree *RedBlackTree) deleteCase6(node *RedBlackTreeNode) { + sibling := node.sibling() + sibling.color = tree.nodeColor(node.parent) + node.parent.color = black + if node == node.parent.left && tree.nodeColor(sibling.right) == red { + sibling.right.color = black + tree.rotateLeft(node.parent) + } else if tree.nodeColor(sibling.left) == red { + sibling.left.color = black + tree.rotateRight(node.parent) + } +} + +func (tree *RedBlackTree) nodeColor(node *RedBlackTreeNode) color { + if node == nil { + return black + } + return node.color +} \ No newline at end of file diff --git a/g/container/gtree/gtree_z_avl_tree_test.go b/g/container/gtree/gtree_z_avl_tree_test.go new file mode 100644 index 000000000..f20ed2601 --- /dev/null +++ b/g/container/gtree/gtree_z_avl_tree_test.go @@ -0,0 +1,103 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gtree_test + +import ( + "github.com/gogf/gf/g/container/gtree" + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/util/gutil" + "testing" +) + + +func Test_AVLTree_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gtree.NewAVLTree(gutil.ComparatorString) + m.Set("key1", "val1") + gtest.Assert(m.Keys(), []interface{}{"key1"}) + + gtest.Assert(m.Get("key1"), "val1") + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("key2", "val2"), "val2") + gtest.Assert(m.SetIfNotExist("key2", "val2"), false) + + gtest.Assert(m.SetIfNotExist("key3", "val3"), true) + + gtest.Assert(m.Remove("key2"), "val2") + gtest.Assert(m.Contains("key2"), false) + + gtest.AssertIN("key3", m.Keys()) + gtest.AssertIN("key1", m.Keys()) + gtest.AssertIN("val3", m.Values()) + gtest.AssertIN("val1", m.Values()) + + m.Flip() + gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gtree.NewAVLTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"}) + gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) + }) +} +func Test_AVLTree_Set_Fun(t *testing.T) { + m := gtree.NewAVLTree(gutil.ComparatorString) + m.GetOrSetFunc("fun", getValue) + m.GetOrSetFuncLock("funlock", getValue) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + m.GetOrSetFunc("fun", getValue) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) +} + +func Test_AVLTree_Batch(t *testing.T) { + m := gtree.NewAVLTree(gutil.ComparatorString) + m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + m.Removes([]interface{}{"key1", 1}) + gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"}) +} +func Test_AVLTree_Iterator(t *testing.T){ + expect := map[interface{}]interface{}{1: 1, "key1": "val1"} + + m := gtree.NewAVLTreeFrom(gutil.ComparatorString, expect) + m.Iterator(func(k interface{}, v interface{}) bool { + gtest.Assert(expect[k], v) + return true + }) + // 断言返回值对遍历控制 + i := 0 + j := 0 + m.Iterator(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.Iterator(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, 2) + gtest.Assert(j, 1) +} + +func Test_AVLTree_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gtree.NewAVLTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"}) + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove("key1") + //修改clone map,原 map 不影响 + gtest.AssertIN("key1", m.Keys()) +} \ No newline at end of file diff --git a/g/container/gtree/gtree_z_b_tree_test.go b/g/container/gtree/gtree_z_b_tree_test.go new file mode 100644 index 000000000..1596bd175 --- /dev/null +++ b/g/container/gtree/gtree_z_b_tree_test.go @@ -0,0 +1,99 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gtree_test + +import ( + "github.com/gogf/gf/g/container/gtree" + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/util/gutil" + "testing" +) + +func Test_BTree_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gtree.NewBTree(3, gutil.ComparatorString) + m.Set("key1", "val1") + gtest.Assert(m.Keys(), []interface{}{"key1"}) + + gtest.Assert(m.Get("key1"), "val1") + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("key2", "val2"), "val2") + gtest.Assert(m.SetIfNotExist("key2", "val2"), false) + + gtest.Assert(m.SetIfNotExist("key3", "val3"), true) + + gtest.Assert(m.Remove("key2"), "val2") + gtest.Assert(m.Contains("key2"), false) + + gtest.AssertIN("key3", m.Keys()) + gtest.AssertIN("key1", m.Keys()) + gtest.AssertIN("val3", m.Values()) + gtest.AssertIN("val1", m.Values()) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gtree.NewBTreeFrom(3, gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"}) + gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) + }) +} +func Test_BTree_Set_Fun(t *testing.T) { + m := gtree.NewBTree(3, gutil.ComparatorString) + m.GetOrSetFunc("fun", getValue) + m.GetOrSetFuncLock("funlock", getValue) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + m.GetOrSetFunc("fun", getValue) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) +} + +func Test_BTree_Batch(t *testing.T) { + m := gtree.NewBTree(3, gutil.ComparatorString) + m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + m.Removes([]interface{}{"key1", 1}) + gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"}) +} +func Test_BTree_Iterator(t *testing.T){ + expect := map[interface{}]interface{}{1: 1, "key1": "val1"} + + m := gtree.NewBTreeFrom(3, gutil.ComparatorString, expect) + m.Iterator(func(k interface{}, v interface{}) bool { + gtest.Assert(expect[k], v) + return true + }) + // 断言返回值对遍历控制 + i := 0 + j := 0 + m.Iterator(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.Iterator(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, 2) + gtest.Assert(j, 1) +} + +func Test_BTree_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gtree.NewBTreeFrom(3, gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"}) + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove("key1") + //修改clone map,原 map 不影响 + gtest.AssertIN("key1", m.Keys()) +} \ No newline at end of file diff --git a/g/container/gtree/gtree_z_redblack_tree_test.go b/g/container/gtree/gtree_z_redblack_tree_test.go new file mode 100644 index 000000000..6dbe85c6a --- /dev/null +++ b/g/container/gtree/gtree_z_redblack_tree_test.go @@ -0,0 +1,106 @@ +// Copyright 2017-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with gm file, +// You can obtain one at https://github.com/gogf/gf. + +package gtree_test + +import ( + "github.com/gogf/gf/g/container/gtree" + "github.com/gogf/gf/g/test/gtest" + "github.com/gogf/gf/g/util/gutil" + "testing" +) + +func getValue() interface{} { + return 3 +} + +func Test_RedBlackTree_Basic(t *testing.T) { + gtest.Case(t, func() { + m := gtree.NewRedBlackTree(gutil.ComparatorString) + m.Set("key1", "val1") + gtest.Assert(m.Keys(), []interface{}{"key1"}) + + gtest.Assert(m.Get("key1"), "val1") + gtest.Assert(m.Size(), 1) + gtest.Assert(m.IsEmpty(), false) + + gtest.Assert(m.GetOrSet("key2", "val2"), "val2") + gtest.Assert(m.SetIfNotExist("key2", "val2"), false) + + gtest.Assert(m.SetIfNotExist("key3", "val3"), true) + + gtest.Assert(m.Remove("key2"), "val2") + gtest.Assert(m.Contains("key2"), false) + + gtest.AssertIN("key3", m.Keys()) + gtest.AssertIN("key1", m.Keys()) + gtest.AssertIN("val3", m.Values()) + gtest.AssertIN("val1", m.Values()) + + m.Flip() + gtest.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"}) + + m.Clear() + gtest.Assert(m.Size(), 0) + gtest.Assert(m.IsEmpty(), true) + + m2 := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"}) + gtest.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"}) + }) +} +func Test_RedBlackTree_Set_Fun(t *testing.T) { + m := gtree.NewRedBlackTree(gutil.ComparatorString) + m.GetOrSetFunc("fun", getValue) + m.GetOrSetFuncLock("funlock", getValue) + gtest.Assert(m.Get("funlock"), 3) + gtest.Assert(m.Get("fun"), 3) + m.GetOrSetFunc("fun", getValue) + gtest.Assert(m.SetIfNotExistFunc("fun", getValue), false) + gtest.Assert(m.SetIfNotExistFuncLock("funlock", getValue), false) +} + +func Test_RedBlackTree_Batch(t *testing.T) { + m := gtree.NewRedBlackTree(gutil.ComparatorString) + m.Sets(map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + gtest.Assert(m.Map(), map[interface{}]interface{}{1: 1, "key1": "val1", "key2": "val2", "key3": "val3"}) + m.Removes([]interface{}{"key1", 1}) + gtest.Assert(m.Map(), map[interface{}]interface{}{"key2": "val2", "key3": "val3"}) +} +func Test_RedBlackTree_Iterator(t *testing.T){ + expect := map[interface{}]interface{}{1: 1, "key1": "val1"} + + m := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, expect) + m.Iterator(func(k interface{}, v interface{}) bool { + gtest.Assert(expect[k], v) + return true + }) + // 断言返回值对遍历控制 + i := 0 + j := 0 + m.Iterator(func(k interface{}, v interface{}) bool { + i++ + return true + }) + m.Iterator(func(k interface{}, v interface{}) bool { + j++ + return false + }) + gtest.Assert(i, 2) + gtest.Assert(j, 1) +} + +func Test_RedBlackTree_Clone(t *testing.T) { + //clone 方法是深克隆 + m := gtree.NewRedBlackTreeFrom(gutil.ComparatorString, map[interface{}]interface{}{1: 1, "key1": "val1"}) + m_clone := m.Clone() + m.Remove(1) + //修改原 map,clone 后的 map 不影响 + gtest.AssertIN(1, m_clone.Keys()) + + m_clone.Remove("key1") + //修改clone map,原 map 不影响 + gtest.AssertIN("key1", m.Keys()) +} \ No newline at end of file diff --git a/g/container/gtype/bool.go b/g/container/gtype/bool.go index e919ce9de..1c5880141 100644 --- a/g/container/gtype/bool.go +++ b/g/container/gtype/bool.go @@ -33,7 +33,7 @@ func (t *Bool) Clone() *Bool { return NewBool(t.Val()) } -// Set atomically stores value into t.valueue and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Bool) Set(value bool) (old bool) { if value { old = atomic.SwapInt32(&t.value, 1) == 1 diff --git a/g/container/gtype/byte.go b/g/container/gtype/byte.go index e6acc2e2a..14d92c7a0 100644 --- a/g/container/gtype/byte.go +++ b/g/container/gtype/byte.go @@ -30,7 +30,7 @@ func (t *Byte) Clone() *Byte { return NewByte(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Byte) Set(value byte) (old byte) { return byte(atomic.SwapInt32(&t.value, int32(value))) } @@ -40,7 +40,7 @@ func (t *Byte) Val() byte { return byte(atomic.LoadInt32(&t.value)) } -// Add atomically adds delta to t.value and returns the new value. +// Add atomically adds to t.value and returns the new value. func (t *Byte) Add(delta int) (new byte) { return byte(atomic.AddInt32(&t.value, int32(delta))) } diff --git a/g/container/gtype/bytes.go b/g/container/gtype/bytes.go index 909b8f41d..b36eecf05 100644 --- a/g/container/gtype/bytes.go +++ b/g/container/gtype/bytes.go @@ -27,7 +27,7 @@ func (t *Bytes) Clone() *Bytes { return NewBytes(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. // Note: The parameter cannot be nil. func (t *Bytes) Set(value []byte) (old []byte) { old = t.Val() diff --git a/g/container/gtype/float32.go b/g/container/gtype/float32.go index 680910770..912c05d89 100644 --- a/g/container/gtype/float32.go +++ b/g/container/gtype/float32.go @@ -32,7 +32,7 @@ func (t *Float32) Clone() *Float32 { return NewFloat32(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Float32) Set(value float32) (old float32) { return math.Float32frombits(atomic.SwapUint32(&t.value, math.Float32bits(value))) } @@ -42,7 +42,7 @@ func (t *Float32) Val() float32 { return math.Float32frombits(atomic.LoadUint32(&t.value)) } -// Add atomically adds delta to t.value and returns the new value. +// Add atomically adds to t.value and returns the new value. func (t *Float32) Add(delta float32) (new float32) { for { old := math.Float32frombits(t.value) diff --git a/g/container/gtype/float64.go b/g/container/gtype/float64.go index 1ffb6a03f..b96419d01 100644 --- a/g/container/gtype/float64.go +++ b/g/container/gtype/float64.go @@ -32,7 +32,7 @@ func (t *Float64) Clone() *Float64 { return NewFloat64(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Float64) Set(value float64) (old float64) { return math.Float64frombits(atomic.SwapUint64(&t.value, math.Float64bits(value))) } @@ -42,7 +42,7 @@ func (t *Float64) Val() float64 { return math.Float64frombits(atomic.LoadUint64(&t.value)) } -// Add atomically adds delta to t.value and returns the new value. +// Add atomically adds to t.value and returns the new value. func (t *Float64) Add(delta float64) (new float64) { for { old := math.Float64frombits(t.value) diff --git a/g/container/gtype/int.go b/g/container/gtype/int.go index 6e240a873..270829e66 100644 --- a/g/container/gtype/int.go +++ b/g/container/gtype/int.go @@ -30,7 +30,7 @@ func (t *Int) Clone() *Int { return NewInt(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Int) Set(value int) (old int) { return int(atomic.SwapInt64(&t.value, int64(value))) } @@ -40,7 +40,7 @@ func (t *Int) Val() int { return int(atomic.LoadInt64(&t.value)) } -// Add atomically adds delta to t.value and returns the new value. +// Add atomically adds to t.value and returns the new value. func (t *Int) Add(delta int) (new int) { return int(atomic.AddInt64(&t.value, int64(delta))) } \ No newline at end of file diff --git a/g/container/gtype/int32.go b/g/container/gtype/int32.go index 7802ebb0d..06517d8bb 100644 --- a/g/container/gtype/int32.go +++ b/g/container/gtype/int32.go @@ -30,7 +30,7 @@ func (t *Int32) Clone() *Int32 { return NewInt32(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Int32) Set(value int32) (old int32) { return atomic.SwapInt32(&t.value, value) } @@ -40,7 +40,7 @@ func (t *Int32) Val() int32 { return atomic.LoadInt32(&t.value) } -// Add atomically adds delta to t.value and returns the new value. +// Add atomically adds to t.value and returns the new value. func (t *Int32) Add(delta int32) (new int32) { return atomic.AddInt32(&t.value, delta) } \ No newline at end of file diff --git a/g/container/gtype/int64.go b/g/container/gtype/int64.go index 9758be4a8..38530dff4 100644 --- a/g/container/gtype/int64.go +++ b/g/container/gtype/int64.go @@ -30,7 +30,7 @@ func (t *Int64) Clone() *Int64 { return NewInt64(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Int64) Set(value int64) (old int64) { return atomic.SwapInt64(&t.value, value) } @@ -40,7 +40,7 @@ func (t *Int64) Val() int64 { return atomic.LoadInt64(&t.value) } -// Add atomically adds delta to t.value and returns the new value. +// Add atomically adds to t.value and returns the new value. func (t *Int64) Add(delta int64) int64 { return atomic.AddInt64(&t.value, delta) } \ No newline at end of file diff --git a/g/container/gtype/interface.go b/g/container/gtype/interface.go index 3dd1b1507..0b510320e 100644 --- a/g/container/gtype/interface.go +++ b/g/container/gtype/interface.go @@ -29,7 +29,7 @@ func (t *Interface) Clone() *Interface { return NewInterface(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. // Note: The parameter cannot be nil. func (t *Interface) Set(value interface{}) (old interface{}) { old = t.Val() diff --git a/g/container/gtype/string.go b/g/container/gtype/string.go index f524d20bb..390a0e507 100644 --- a/g/container/gtype/string.go +++ b/g/container/gtype/string.go @@ -29,7 +29,7 @@ func (t *String) Clone() *String { return NewString(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *String) Set(value string) (old string) { old = t.Val() t.value.Store(value) diff --git a/g/container/gtype/uint.go b/g/container/gtype/uint.go index c9b5ad3d8..3bc0b28ce 100644 --- a/g/container/gtype/uint.go +++ b/g/container/gtype/uint.go @@ -30,7 +30,7 @@ func (t *Uint) Clone() *Uint { return NewUint(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Uint) Set(value uint) (old uint) { return uint(atomic.SwapUint64(&t.value, uint64(value))) } @@ -40,7 +40,7 @@ func (t *Uint) Val() uint { return uint(atomic.LoadUint64(&t.value)) } -// Add atomically adds delta to t.value and returns the new value. +// Add atomically adds to t.value and returns the new value. func (t *Uint) Add(delta uint) (new uint) { return uint(atomic.AddUint64(&t.value, uint64(delta))) } \ No newline at end of file diff --git a/g/container/gtype/uint32.go b/g/container/gtype/uint32.go index c3a18c904..35db55b86 100644 --- a/g/container/gtype/uint32.go +++ b/g/container/gtype/uint32.go @@ -30,7 +30,7 @@ func (t *Uint32) Clone() *Uint32 { return NewUint32(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Uint32) Set(value uint32) (old uint32) { return atomic.SwapUint32(&t.value, value) } @@ -40,7 +40,7 @@ func (t *Uint32) Val() uint32 { return atomic.LoadUint32(&t.value) } -// Add atomically adds delta to t.value and returns the new value. +// Add atomically adds to t.value and returns the new value. func (t *Uint32) Add(delta uint32) (new uint32) { return atomic.AddUint32(&t.value, delta) } \ No newline at end of file diff --git a/g/container/gtype/uint64.go b/g/container/gtype/uint64.go index 9e63234e0..923068398 100644 --- a/g/container/gtype/uint64.go +++ b/g/container/gtype/uint64.go @@ -30,7 +30,7 @@ func (t *Uint64) Clone() *Uint64 { return NewUint64(t.Val()) } -// Set atomically stores value into t.value and returns the previous t.value value. +// Set atomically stores into t.value and returns the previous value of t.value. func (t *Uint64) Set(value uint64) (old uint64) { return atomic.SwapUint64(&t.value, value) } @@ -40,7 +40,7 @@ func (t *Uint64) Val() uint64 { return atomic.LoadUint64(&t.value) } -// Add atomically adds delta to t.value and returns the new value. +// Add atomically adds to t.value and returns the new value. func (t *Uint64) Add(delta uint64) (new uint64) { return atomic.AddUint64(&t.value, delta) } \ No newline at end of file diff --git a/g/container/gvar/gvar.go b/g/container/gvar/gvar.go index 90f7d684e..16e573fe0 100644 --- a/g/container/gvar/gvar.go +++ b/g/container/gvar/gvar.go @@ -1,4 +1,4 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright 2018-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -15,11 +15,13 @@ import ( ) type Var struct { - value interface{} // 变量值 - safe bool // 当为true时, value为 *gtype.Interface 类型 + value interface{} // Underlying value. + safe bool // Concurrent safe or not. } -// 创建一个动态变量,value参数可以为nil +// New returns a new Var with given . +// The param used to specify whether using Var in un-concurrent-safety, +// which is false in default, means concurrent-safe. func New(value interface{}, unsafe...bool) *Var { v := &Var{} if len(unsafe) == 0 || !unsafe[0] { @@ -31,16 +33,7 @@ func New(value interface{}, unsafe...bool) *Var { return v } -// 创建一个只读动态变量,value参数可以为nil -func NewRead(value interface{}, unsafe...bool) VarRead { - return VarRead(New(value, unsafe...)) -} - -// 返回动态变量的只读接口 -func (v *Var) ReadOnly() VarRead { - return VarRead(v) -} - +// Set sets to , and returns the old value. func (v *Var) Set(value interface{}) (old interface{}) { if v.safe { old = v.value.(*gtype.Interface).Set(value) @@ -51,6 +44,7 @@ func (v *Var) Set(value interface{}) (old interface{}) { return } +// Val returns the current value of . func (v *Var) Val() interface{} { if v.safe { return v.value.(*gtype.Interface).Val() @@ -59,11 +53,38 @@ func (v *Var) Val() interface{} { } } -// Val() 别名 +// See Val(). func (v *Var) Interface() interface{} { return v.Val() } +// Time converts and returns as time.Time. +// The param specifies the format of the time string using gtime, +// eg: Y-m-d H:i:s. +func (v *Var) Time(format...string) time.Time { + return gconv.Time(v.Val(), format...) +} + +// TimeDuration converts and returns as time.Duration. +// If value of is string, then it uses time.ParseDuration for conversion. +func (v *Var) Duration() time.Duration { + return gconv.Duration(v.Val()) +} + +// GTime converts and returns as *gtime.Time. +// The param specifies the format of the time string using gtime, +// eg: Y-m-d H:i:s. +func (v *Var) GTime(format...string) *gtime.Time { + return gconv.GTime(v.Val(), format...) +} + +// Struct maps value of to . +// The param should be a pointer to a struct instance. +// The param is used to specify the key-to-attribute mapping rules. +func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error { + return gconv.Struct(v.Val(), objPointer, attrMapping...) +} + func (v *Var) IsNil() bool { return v.Val() == nil } func (v *Var) Bytes() []byte { return gconv.Bytes(v.Val()) } func (v *Var) String() string { return gconv.String(v.Val()) } @@ -88,19 +109,3 @@ func (v *Var) Ints() []int { return gconv.Ints(v.Val()) } func (v *Var) Floats() []float64 { return gconv.Floats(v.Val()) } func (v *Var) Strings() []string { return gconv.Strings(v.Val()) } func (v *Var) Interfaces() []interface{} { return gconv.Interfaces(v.Val()) } - -func (v *Var) Time(format...string) time.Time { - return gconv.Time(v.Val(), format...) -} -func (v *Var) TimeDuration() time.Duration { - return gconv.TimeDuration(v.Val()) -} - -func (v *Var) GTime(format...string) *gtime.Time { - return gconv.GTime(v.Val(), format...) -} - -// 将变量转换为对象,注意 objPointer 参数必须为struct指针 -func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error { - return gconv.Struct(v.Val(), objPointer, attrMapping...) -} \ No newline at end of file diff --git a/g/container/gvar/gvar_read.go b/g/container/gvar/gvar_read.go deleted file mode 100644 index 0418f6cfd..000000000 --- a/g/container/gvar/gvar_read.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gvar - -import ( - "github.com/gogf/gf/g/os/gtime" - "time" -) - -// 只读变量接口 -type VarRead interface { - Val() interface{} - IsNil() bool - Bytes() []byte - String() string - Bool() bool - Int() int - Int8() int8 - Int16() int16 - Int32() int32 - Int64() int64 - Uint() uint - Uint8() uint8 - Uint16() uint16 - Uint32() uint32 - Uint64() uint64 - Float32() float32 - Float64() float64 - Interface() interface{} - Ints() []int - Floats() []float64 - Strings() []string - Interfaces() []interface{} - Time(format ...string) time.Time - TimeDuration() time.Duration - GTime(format...string) *gtime.Time - Struct(objPointer interface{}, attrMapping ...map[string]string) error -} \ No newline at end of file diff --git a/g/container/gvar/gvar_test.go b/g/container/gvar/gvar_test.go index 8af27577f..68fe77131 100644 --- a/g/container/gvar/gvar_test.go +++ b/g/container/gvar/gvar_test.go @@ -1,3 +1,9 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + package gvar_test import ( @@ -9,22 +15,6 @@ import ( "github.com/gogf/gf/g/test/gtest" ) -func TestReadOnly(t *testing.T) { - gtest.Case(t, func() { - obj := gvar.New(nil, true) - var result string - - switch obj.ReadOnly().(type) { - case gvar.VarRead: - result = "yes" - default: - result = "no" - } - - gtest.Assert(result, "yes") - }) -} - func TestSet(t *testing.T) { gtest.Case(t, func() { objOne := gvar.New("old", true) diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index 3d6d855ce..f1c2b2e2b 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -157,7 +157,7 @@ const ( var ( // Instance map. - instances = gmap.NewStringInterfaceMap() + instances = gmap.NewStrAnyMap() ) // New creates ORM DB object with global configurations. diff --git a/g/database/gredis/gredis.go b/g/database/gredis/gredis.go index b23bf0c42..aefc92e77 100644 --- a/g/database/gredis/gredis.go +++ b/g/database/gredis/gredis.go @@ -6,9 +6,9 @@ // Package gredis provides convenient client for redis server. // -// Redis客户端. -// Redis中文手册请参考:http://redisdoc.com/ -// Redis官方命令请参考:https://redis.io/commands +// Redis Client. +// Redis Commands Official: https://redis.io/commands +// Redis Chinese Documentation: http://redisdoc.com/ package gredis import ( @@ -23,44 +23,42 @@ const ( gDEFAULT_POOL_MAX_LIFE_TIME = 60 * time.Second ) -// Redis客户端(管理连接池) +// Redis client. type Redis struct { - pool *redis.Pool // 底层连接池 - group string // 配置分组 - config Config // 配置对象 + pool *redis.Pool // Underlying connection pool. + group string // Configuration group. + config Config // Configuration. } -// Redis连接对象(连接池中的单个连接) +// Redis connection. type Conn redis.Conn -// Redis服务端但节点连接配置信息 +// Redis configuration. type Config struct { - Host string // 地址 - Port int // 端口 - Db int // 数据库 - Pass string // 授权密码 - MaxIdle int // 最大允许空闲存在的连接数(默认为0表示不存在闲置连接) - MaxActive int // 最大连接数量限制(默认为0表示不限制) - IdleTimeout time.Duration // 连接最大空闲时间(默认为60秒,不允许设置为0) - MaxConnLifetime time.Duration // 连接最长存活时间(默认为60秒,不允许设置为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) } -// Redis链接池统计信息 +// Pool statistics. type PoolStats struct { redis.PoolStats } var ( - // 单例对象Map - instances = gmap.NewStringInterfaceMap() - // 连接池Map - pools = gmap.NewStringInterfaceMap() + // 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. -// -// 创建redis操作对象,底层根据配置信息公用的连接池(连接池单例)。 func New(config Config) *Redis { if config.IdleTimeout == 0 { config.IdleTimeout = gDEFAULT_POOL_IDLE_TIMEOUT @@ -79,20 +77,20 @@ func New(config Config) *Redis { 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 }, - // 在被应用从连接池中获取出来之后,用以测试连接是否可用, - // 如果返回error那么关闭该连接对象重新创建新的连接。 + // 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 @@ -104,9 +102,7 @@ func New(config Config) *Redis { // Instance returns an instance of redis client with specified group. // The param is unnecessary, if is not passed, -// return redis instance with default group. -// -// 获取指定分组名称的Redis单例对象,底层根据配置信息公用的连接池(连接池单例)。 +// it returns a redis instance with default group. func Instance(name ...string) *Redis { group := DEFAULT_GROUP_NAME if len(name) > 0 { @@ -128,73 +124,56 @@ func Instance(name ...string) *Redis { // Close closes the redis connection pool, // it will release all connections reserved by this pool. -// It always not necessary to call Close manually. -// -// 关闭redis管理对象,将会关闭底层的连接池。 -// 往往没必要手动调用,跟随进程销毁即可。 +// It is not necessary to call Close manually. func (r *Redis) Close() error { if r.group != "" { - // 如果是单例对象,那么需要从单例对象Map中删除 + // 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() } -// See GetConn. +// Alias of GetConn, see GetConn. func (r *Redis) Conn() Conn { return r.GetConn() } -// GetConn returns a raw connection object, -// which expose more methods communication with server. +// GetConn 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.** -// -// 获得一个原生的redis连接对象,用于自定义连接操作, -// 但是需要注意的是如果不再使用该连接对象时,需要手动Close连接,否则会造成连接数超限。 func (r *Redis) GetConn() Conn { return r.pool.Get().(Conn) } // SetMaxIdle sets the MaxIdle attribute of the connection pool. -// -// 设置属性 - MaxIdle func (r *Redis) SetMaxIdle(value int) { r.pool.MaxIdle = value } -// SetMaxIdle sets the MaxActive attribute of the connection pool. -// -// 设置属性 - MaxActive +// SetMaxActive sets the MaxActive attribute of the connection pool. func (r *Redis) SetMaxActive(value int) { r.pool.MaxActive = value } -// SetMaxIdle sets the IdleTimeout attribute of the connection pool. -// -// 设置属性 - IdleTimeout +// SetIdleTimeout sets the IdleTimeout attribute of the connection pool. func (r *Redis) SetIdleTimeout(value time.Duration) { r.pool.IdleTimeout = value } -// SetMaxIdle sets the MaxConnLifetime attribute of the connection pool. -// -// 设置属性 - MaxConnLifetime +// SetMaxConnLifetime sets the MaxConnLifetime attribute of the connection pool. func (r *Redis) SetMaxConnLifetime(value time.Duration) { r.pool.MaxConnLifetime = value } // Stats returns pool's statistics. -// -// 获取当前连接池统计信息。 func (r *Redis) Stats() *PoolStats { 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. -// -// 执行同步命令,自动从连接池中获取连接,使用完毕后关闭连接(丢回连接池),开发者不用自行Close. +// 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 := r.pool.Get() defer conn.Close() @@ -203,8 +182,6 @@ func (r *Redis) Do(command string, args ...interface{}) (interface{}, error) { // Deprecated. // Send writes the command to the client's output buffer. -// -// 执行异步命令 - Send func (r *Redis) Send(command string, args ...interface{}) error { conn := r.pool.Get() defer conn.Close() diff --git a/g/database/gredis/gredis_config.go b/g/database/gredis/gredis_config.go index 1f1dba16f..c8ab71900 100644 --- a/g/database/gredis/gredis_config.go +++ b/g/database/gredis/gredis_config.go @@ -14,7 +14,7 @@ const ( ) var ( // Configuration groups. - configs = gmap.NewStringInterfaceMap() + configs = gmap.NewStrAnyMap() ) // SetConfig sets the global configuration for specified group. diff --git a/g/encoding/gjson/gjson.go b/g/encoding/gjson/gjson.go index da2f1e226..3b63289bd 100644 --- a/g/encoding/gjson/gjson.go +++ b/g/encoding/gjson/gjson.go @@ -8,22 +8,12 @@ package gjson import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "github.com/gogf/gf/g/encoding/gtoml" - "github.com/gogf/gf/g/encoding/gxml" - "github.com/gogf/gf/g/encoding/gyaml" - "github.com/gogf/gf/g/internal/rwmutex" - "github.com/gogf/gf/g/os/gfcache" - "github.com/gogf/gf/g/text/gregex" - "github.com/gogf/gf/g/text/gstr" - "github.com/gogf/gf/g/util/gconv" - "reflect" - "strconv" - "strings" - "time" + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/text/gstr" + "github.com/gogf/gf/g/util/gconv" + "reflect" + "strconv" + "strings" ) const ( @@ -40,330 +30,6 @@ type Json struct { // when the hierarchical data key contains separator char. } -// New creates a Json object with any variable type of , -// but should be a map or slice for data access reason, -// or it will make no sense. -// The param specifies whether using this Json object -// in un-concurrent-safe context, which is false in default. -func New(data interface{}, unsafe...bool) *Json { - j := (*Json)(nil) - switch data.(type) { - case map[string]interface{}, []interface{}, nil: - j = &Json { - p : &data, - c : byte(gDEFAULT_SPLIT_CHAR), - vc : false , - } - case string, []byte: - j, _ = LoadContent(gconv.Bytes(data)) - default: - v := (interface{})(nil) - if m := gconv.Map(data); m != nil { - v = m - j = &Json { - p : &v, - c : byte(gDEFAULT_SPLIT_CHAR), - vc : false, - } - } else { - v = gconv.Interfaces(data) - j = &Json { - p : &v, - c : byte(gDEFAULT_SPLIT_CHAR), - vc : false, - } - } - } - j.mu = rwmutex.New(unsafe...) - return j -} - -// NewUnsafe creates a un-concurrent-safe Json object. -func NewUnsafe(data...interface{}) *Json { - if len(data) > 0 { - return New(data[0], true) - } - return New(nil, true) -} - -// Valid checks whether is a valid JSON data type. -func Valid(data interface{}) bool { - return json.Valid(gconv.Bytes(data)) -} - -// Encode encodes to JSON data type of bytes. -func Encode(value interface{}) ([]byte, error) { - return json.Marshal(value) -} - -// Decode decodes (string/[]byte) to golang variable. -func Decode(data interface{}) (interface{}, error) { - var value interface{} - if err := DecodeTo(gconv.Bytes(data), &value); err != nil { - return nil, err - } else { - return value, nil - } -} - -// Decode decodes (string/[]byte) to specified golang variable . -// The should be a pointer type. -func DecodeTo(data interface{}, v interface{}) error { - decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data))) - decoder.UseNumber() - return decoder.Decode(v) -} - -// DecodeToJson codes (string/[]byte) to a Json object. -func DecodeToJson(data interface{}, unsafe...bool) (*Json, error) { - if v, err := Decode(gconv.Bytes(data)); err != nil { - return nil, err - } else { - return New(v, unsafe...), nil - } -} - -// Load loads content from specified file , -// and creates a Json object from its content. -func Load(path string, unsafe...bool) (*Json, error) { - return LoadContent(gfcache.GetBinContents(path), unsafe...) -} - -// LoadContent creates a Json object from given content, -// it checks the data type of automatically, -// supporting JSON, XML, YAML and TOML types of data. -func LoadContent(data interface{}, unsafe...bool) (*Json, error) { - var err error - var result interface{} - b := gconv.Bytes(data) - t := "json" - // auto check data type - if json.Valid(b) { - t = "json" - } else if gregex.IsMatch(`^<.+>.*$`, b) { - t = "xml" - } else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) { - t = "yml" - } else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) { - t = "toml" - } else { - return nil, errors.New("unsupported data type") - } - // convert to json type data - switch t { - case "json", ".json": - // ok - case "xml", ".xml": - // TODO UseNumber - b, err = gxml.ToJson(b) - - case "yml", "yaml", ".yml", ".yaml": - // TODO UseNumber - b, err = gyaml.ToJson(b) - - case "toml", ".toml": - // TODO UseNumber - b, err = gtoml.ToJson(b) - - default: - err = errors.New("nonsupport type " + t) - } - if err != nil { - return nil, err - } - if result == nil { - decoder := json.NewDecoder(bytes.NewReader(b)) - decoder.UseNumber() - if err := decoder.Decode(&result); err != nil { - return nil, err - } - switch result.(type) { - case string, []byte: - return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b)) - } - } - return New(result, unsafe...), nil -} - -// SetSplitChar sets the separator char for hierarchical data access. -func (j *Json) SetSplitChar(char byte) { - j.mu.Lock() - j.c = char - j.mu.Unlock() -} - -// SetViolenceCheck enables/disables violence check for hierarchical data access. -func (j *Json) SetViolenceCheck(enabled bool) { - j.mu.Lock() - j.vc = enabled - j.mu.Unlock() -} - -// GetToVar gets the value by specified , -// and converts it to specified golang variable . -// The should be a pointer type. -func (j *Json) GetToVar(pattern string, v interface{}) error { - r := j.Get(pattern) - if r != nil { - if t, err := Encode(r); err == nil { - return DecodeTo(t, v) - } else { - return err - } - } else { - v = nil - } - return nil -} - -// GetMap gets the value by specified , -// and converts it to map[string]interface{}. -func (j *Json) GetMap(pattern string) map[string]interface{} { - result := j.Get(pattern) - if result != nil { - return gconv.Map(result) - } - return nil -} - -// GetJson gets the value by specified , -// and converts it to a Json object. -func (j *Json) GetJson(pattern string) *Json { - result := j.Get(pattern) - if result != nil { - return New(result) - } - return nil -} - -// GetJsons gets the value by specified , -// and converts it to a slice of Json object. -func (j *Json) GetJsons(pattern string) []*Json { - array := j.GetArray(pattern) - if len(array) > 0 { - jsons := make([]*Json, len(array)) - for i := 0; i < len(array); i++ { - jsons[i] = New(array[i], !j.mu.IsSafe()) - } - return jsons - } - return nil -} - -// GetArray gets the value by specified , -// and converts it to a slice of []interface{}. -func (j *Json) GetArray(pattern string) []interface{} { - return gconv.Interfaces(j.Get(pattern)) -} - -// GetString gets the value by specified , -// and converts it to string. -func (j *Json) GetString(pattern string) string { - return gconv.String(j.Get(pattern)) -} - -// GetStrings gets the value by specified , -// and converts it to a slice of []string. -func (j *Json) GetStrings(pattern string) []string { - return gconv.Strings(j.Get(pattern)) -} - -// See GetArray. -func (j *Json) GetInterfaces(pattern string) []interface{} { - return gconv.Interfaces(j.Get(pattern)) -} - -func (j *Json) GetTime(pattern string, format ... string) time.Time { - return gconv.Time(j.Get(pattern), format...) -} - -func (j *Json) GetTimeDuration(pattern string) time.Duration { - return gconv.TimeDuration(j.Get(pattern)) -} - -// GetBool gets the value by specified , -// and converts it to bool. -// It returns false when value is: "", 0, false, off, nil; -// or returns true instead. -func (j *Json) GetBool(pattern string) bool { - return gconv.Bool(j.Get(pattern)) -} - -func (j *Json) GetInt(pattern string) int { - return gconv.Int(j.Get(pattern)) -} - -func (j *Json) GetInt8(pattern string) int8 { - return gconv.Int8(j.Get(pattern)) -} - -func (j *Json) GetInt16(pattern string) int16 { - return gconv.Int16(j.Get(pattern)) -} - -func (j *Json) GetInt32(pattern string) int32 { - return gconv.Int32(j.Get(pattern)) -} - -func (j *Json) GetInt64(pattern string) int64 { - return gconv.Int64(j.Get(pattern)) -} - -func (j *Json) GetInts(pattern string) []int { - return gconv.Ints(j.Get(pattern)) -} - -func (j *Json) GetUint(pattern string) uint { - return gconv.Uint(j.Get(pattern)) -} - -func (j *Json) GetUint8(pattern string) uint8 { - return gconv.Uint8(j.Get(pattern)) -} - -func (j *Json) GetUint16(pattern string) uint16 { - return gconv.Uint16(j.Get(pattern)) -} - -func (j *Json) GetUint32(pattern string) uint32 { - return gconv.Uint32(j.Get(pattern)) -} - -func (j *Json) GetUint64(pattern string) uint64 { - return gconv.Uint64(j.Get(pattern)) -} - -func (j *Json) GetFloat32(pattern string) float32 { - return gconv.Float32(j.Get(pattern)) -} - -func (j *Json) GetFloat64(pattern string) float64 { - return gconv.Float64(j.Get(pattern)) -} - -func (j *Json) GetFloats(pattern string) []float64 { - return gconv.Floats(j.Get(pattern)) -} - -// GetToStruct gets the value by specified , -// and converts it to specified object . -// The should be the pointer to an object. -func (j *Json) GetToStruct(pattern string, objPointer interface{}) error { - return gconv.Struct(j.Get(pattern), objPointer) -} - -// Set sets value with specified . -// It supports hierarchical data access by char separator, which is '.' in default. -func (j *Json) Set(pattern string, value interface{}) error { - return j.setValue(pattern, value, false) -} - -// Remove deletes value with specified . -// It supports hierarchical data access by char separator, which is '.' in default. -func (j *Json) Remove(pattern string) error { - return j.setValue(pattern, nil, true) -} - // Set by . // Notice: // 1. If value is nil and removed is true, means deleting this value; @@ -576,69 +242,6 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter return pointer } -// Get returns value by specified . -// It returns all values of current Json object, if is empty or not specified. -// It returns nil if no value found by . -// -// We can also access slice item by its index number in , -// eg: "items.name.first", "list.10". -func (j *Json) Get(pattern...string) interface{} { - j.mu.RLock() - defer j.mu.RUnlock() - - queryPattern := "" - if len(pattern) > 0 { - queryPattern = pattern[0] - } - var result *interface{} - if j.vc { - result = j.getPointerByPattern(queryPattern) - } else { - result = j.getPointerByPatternWithoutViolenceCheck(queryPattern) - } - if result != nil { - return *result - } - return nil -} - -// Contains checks whether the value by specified exist. -func (j *Json) Contains(pattern...string) bool { - return j.Get(pattern...) != nil -} - -// Len returns the length/size of the value by specified . -// The target value by should be type of slice or map. -// It returns -1 if the target value is not found, or its type is invalid. -func (j *Json) Len(pattern string) int { - p := j.getPointerByPattern(pattern) - if p != nil { - switch (*p).(type) { - case map[string]interface{}: - return len((*p).(map[string]interface{})) - case []interface{}: - return len((*p).([]interface{})) - default: - return -1 - } - } - return -1 -} - -// Append appends value to the value by specified . -// The target value by should be type of slice. -func (j *Json) Append(pattern string, value interface{}) error { - p := j.getPointerByPattern(pattern) - if p == nil { - return j.Set(fmt.Sprintf("%s.0", pattern), value) - } - switch (*p).(type) { - case []interface{}: - return j.Set(fmt.Sprintf("%s.%d", pattern, len((*p).([]interface{}))), value) - } - return fmt.Errorf("invalid variable type of %s", pattern) -} - // Get a pointer to the value by specified . func (j *Json) getPointerByPattern(pattern string) *interface{} { if j.vc { @@ -729,111 +332,3 @@ func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interfac } return nil } - -// ToMap converts current Json object to map[string]interface{}. -// It returns nil if fails. -func (j *Json) ToMap() map[string]interface{} { - j.mu.RLock() - defer j.mu.RUnlock() - switch (*(j.p)).(type) { - case map[string]interface{}: - return (*(j.p)).(map[string]interface{}) - default: - return nil - } -} - -// ToArray converts current Json object to []interface{}. -// It returns nil if fails. -func (j *Json) ToArray() []interface{} { - j.mu.RLock() - defer j.mu.RUnlock() - switch (*(j.p)).(type) { - case []interface{}: - return (*(j.p)).([]interface{}) - default: - return nil - } -} - -func (j *Json) ToXml(rootTag...string) ([]byte, error) { - return gxml.Encode(j.ToMap(), rootTag...) -} - -func (j *Json) ToXmlString(rootTag...string) (string, error) { - b, e := j.ToXml(rootTag...) - return string(b), e -} - -func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) { - return gxml.EncodeWithIndent(j.ToMap(), rootTag...) -} - -func (j *Json) ToXmlIndentString(rootTag...string) (string, error) { - b, e := j.ToXmlIndent(rootTag...) - return string(b), e -} - -func (j *Json) ToJson() ([]byte, error) { - j.mu.RLock() - defer j.mu.RUnlock() - return Encode(*(j.p)) -} - -func (j *Json) ToJsonString() (string, error) { - b, e := j.ToJson() - return string(b), e -} - -func (j *Json) ToJsonIndent() ([]byte, error) { - j.mu.RLock() - defer j.mu.RUnlock() - return json.MarshalIndent(*(j.p), "", "\t") -} - -func (j *Json) ToJsonIndentString() (string, error) { - b, e := j.ToJsonIndent() - return string(b), e -} - -func (j *Json) ToYaml() ([]byte, error) { - j.mu.RLock() - defer j.mu.RUnlock() - return gyaml.Encode(*(j.p)) -} - -func (j *Json) ToYamlString() (string, error) { - b, e := j.ToYaml() - return string(b), e -} - -func (j *Json) ToToml() ([]byte, error) { - j.mu.RLock() - defer j.mu.RUnlock() - return gtoml.Encode(*(j.p)) -} - -func (j *Json) ToTomlString() (string, error) { - b, e := j.ToToml() - return string(b), e -} - -// ToStruct converts current Json object to specified object. -// The should be a pointer type. -func (j *Json) ToStruct(objPointer interface{}) error { - j.mu.RLock() - defer j.mu.RUnlock() - return gconv.Struct(*(j.p), objPointer) -} - -// Dump prints current Json object with more manually readable. -func (j *Json) Dump() error { - j.mu.RLock() - defer j.mu.RUnlock() - if b, err := j.ToJsonIndent(); err != nil { - return err - } else { - fmt.Println(string(b)) - } - return nil -} \ No newline at end of file diff --git a/g/encoding/gjson/gjson_api.go b/g/encoding/gjson/gjson_api.go new file mode 100644 index 000000000..028478b8e --- /dev/null +++ b/g/encoding/gjson/gjson_api.go @@ -0,0 +1,296 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gjson + +import ( + "fmt" + "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/util/gconv" + "time" +) + +// Val returns the json value. +func (j *Json) Value() interface{} { + j.mu.RLock() + defer j.mu.RUnlock() + return *(j.p) +} + +// Get returns value by specified . +// It returns all values of current Json object, if is empty or not specified. +// It returns nil if no value found by . +// +// We can also access slice item by its index number in , +// eg: "items.name.first", "list.10". +// +// It returns a default value specified by if value for is not found. +func (j *Json) Get(pattern string, def...interface{}) interface{} { + j.mu.RLock() + defer j.mu.RUnlock() + + var result *interface{} + if j.vc { + result = j.getPointerByPattern(pattern) + } else { + result = j.getPointerByPatternWithoutViolenceCheck(pattern) + } + if result != nil { + return *result + } + if len(def) > 0 { + return def[0] + } + return nil +} + +// GetVar returns a *gvar.Var with value by given . +func (j *Json) GetVar(pattern string, def...interface{}) *gvar.Var { + return gvar.New(j.Get(pattern, def...), true) +} + +// GetMap gets the value by specified , +// and converts it to map[string]interface{}. +func (j *Json) GetMap(pattern string, def...interface{}) map[string]interface{} { + result := j.Get(pattern, def...) + if result != nil { + return gconv.Map(result) + } + return nil +} + +// GetJson gets the value by specified , +// and converts it to a Json object. +func (j *Json) GetJson(pattern string, def...interface{}) *Json { + result := j.Get(pattern, def...) + if result != nil { + return New(result) + } + return nil +} + +// GetJsons gets the value by specified , +// and converts it to a slice of Json object. +func (j *Json) GetJsons(pattern string, def...interface{}) []*Json { + array := j.GetArray(pattern, def...) + if len(array) > 0 { + jsons := make([]*Json, len(array)) + for i := 0; i < len(array); i++ { + jsons[i] = New(array[i], !j.mu.IsSafe()) + } + return jsons + } + return nil +} + +// GetArray gets the value by specified , +// and converts it to a slice of []interface{}. +func (j *Json) GetArray(pattern string, def...interface{}) []interface{} { + return gconv.Interfaces(j.Get(pattern, def...)) +} + +// GetString gets the value by specified , +// and converts it to string. +func (j *Json) GetString(pattern string, def...interface{}) string { + return gconv.String(j.Get(pattern, def...)) +} + +// GetBool gets the value by specified , +// and converts it to bool. +// It returns false when value is: "", 0, false, off, nil; +// or returns true instead. +func (j *Json) GetBool(pattern string, def...interface{}) bool { + return gconv.Bool(j.Get(pattern, def...)) +} + +func (j *Json) GetInt(pattern string, def...interface{}) int { + return gconv.Int(j.Get(pattern, def...)) +} + +func (j *Json) GetInt8(pattern string, def...interface{}) int8 { + return gconv.Int8(j.Get(pattern, def...)) +} + +func (j *Json) GetInt16(pattern string, def...interface{}) int16 { + return gconv.Int16(j.Get(pattern, def...)) +} + +func (j *Json) GetInt32(pattern string, def...interface{}) int32 { + return gconv.Int32(j.Get(pattern, def...)) +} + +func (j *Json) GetInt64(pattern string, def...interface{}) int64 { + return gconv.Int64(j.Get(pattern, def...)) +} + +func (j *Json) GetUint(pattern string, def...interface{}) uint { + return gconv.Uint(j.Get(pattern, def...)) +} + +func (j *Json) GetUint8(pattern string, def...interface{}) uint8 { + return gconv.Uint8(j.Get(pattern, def...)) +} + +func (j *Json) GetUint16(pattern string, def...interface{}) uint16 { + return gconv.Uint16(j.Get(pattern, def...)) +} + +func (j *Json) GetUint32(pattern string, def...interface{}) uint32 { + return gconv.Uint32(j.Get(pattern, def...)) +} + +func (j *Json) GetUint64(pattern string, def...interface{}) uint64 { + return gconv.Uint64(j.Get(pattern, def...)) +} + +func (j *Json) GetFloat32(pattern string, def...interface{}) float32 { + return gconv.Float32(j.Get(pattern, def...)) +} + +func (j *Json) GetFloat64(pattern string, def...interface{}) float64 { + return gconv.Float64(j.Get(pattern, def...)) +} + +func (j *Json) GetFloats(pattern string, def...interface{}) []float64 { + return gconv.Floats(j.Get(pattern, def...)) +} + +func (j *Json) GetInts(pattern string, def...interface{}) []int { + return gconv.Ints(j.Get(pattern, def...)) +} + +// GetStrings gets the value by specified , +// and converts it to a slice of []string. +func (j *Json) GetStrings(pattern string, def...interface{}) []string { + return gconv.Strings(j.Get(pattern, def...)) +} + +// See GetArray. +func (j *Json) GetInterfaces(pattern string, def...interface{}) []interface{} { + return gconv.Interfaces(j.Get(pattern, def...)) +} + +func (j *Json) GetTime(pattern string, format... string) time.Time { + return gconv.Time(j.Get(pattern), format...) +} + +func (j *Json) GetDuration(pattern string, def...interface{}) time.Duration { + return gconv.Duration(j.Get(pattern, def...)) +} + +func (j *Json) GetGTime(pattern string, format... string) *gtime.Time { + return gconv.GTime(j.Get(pattern), format...) +} + +// Set sets value with specified . +// It supports hierarchical data access by char separator, which is '.' in default. +func (j *Json) Set(pattern string, value interface{}) error { + return j.setValue(pattern, value, false) +} + +// Remove deletes value with specified . +// It supports hierarchical data access by char separator, which is '.' in default. +func (j *Json) Remove(pattern string) error { + return j.setValue(pattern, nil, true) +} + +// Contains checks whether the value by specified exist. +func (j *Json) Contains(pattern string) bool { + return j.Get(pattern) != nil +} + +// Len returns the length/size of the value by specified . +// The target value by should be type of slice or map. +// It returns -1 if the target value is not found, or its type is invalid. +func (j *Json) Len(pattern string) int { + p := j.getPointerByPattern(pattern) + if p != nil { + switch (*p).(type) { + case map[string]interface{}: + return len((*p).(map[string]interface{})) + case []interface{}: + return len((*p).([]interface{})) + default: + return -1 + } + } + return -1 +} + +// Append appends value to the value by specified . +// The target value by should be type of slice. +func (j *Json) Append(pattern string, value interface{}) error { + p := j.getPointerByPattern(pattern) + if p == nil { + return j.Set(fmt.Sprintf("%s.0", pattern), value) + } + switch (*p).(type) { + case []interface{}: + return j.Set(fmt.Sprintf("%s.%d", pattern, len((*p).([]interface{}))), value) + } + return fmt.Errorf("invalid variable type of %s", pattern) +} + +// GetToVar gets the value by specified , +// and converts it to specified golang variable . +// The should be a pointer type. +func (j *Json) GetToVar(pattern string, pointer interface{}) error { + r := j.Get(pattern) + if r != nil { + if t, err := Encode(r); err == nil { + return DecodeTo(t, pointer) + } else { + return err + } + } else { + pointer = nil + } + return nil +} + +// GetToStruct gets the value by specified , +// and converts it to specified object . +// The should be the pointer to an object. +func (j *Json) GetToStruct(pattern string, pointer interface{}) error { + return gconv.Struct(j.Get(pattern), pointer) +} + +// ToMap converts current Json object to map[string]interface{}. +// It returns nil if fails. +func (j *Json) ToMap() map[string]interface{} { + j.mu.RLock() + defer j.mu.RUnlock() + return gconv.Map(*(j.p)) +} + +// ToArray converts current Json object to []interface{}. +// It returns nil if fails. +func (j *Json) ToArray() []interface{} { + j.mu.RLock() + defer j.mu.RUnlock() + return gconv.Interfaces(*(j.p)) +} + +// ToStruct converts current Json object to specified object. +// The should be a pointer type. +func (j *Json) ToStruct(pointer interface{}) error { + j.mu.RLock() + defer j.mu.RUnlock() + return gconv.Struct(*(j.p), pointer) +} + +// Dump prints current Json object with more manually readable. +func (j *Json) Dump() error { + j.mu.RLock() + defer j.mu.RUnlock() + if b, err := j.ToJsonIndent(); err != nil { + return err + } else { + fmt.Println(string(b)) + } + return nil +} \ No newline at end of file diff --git a/g/encoding/gjson/gjson_api_config.go b/g/encoding/gjson/gjson_api_config.go new file mode 100644 index 000000000..82c11b32b --- /dev/null +++ b/g/encoding/gjson/gjson_api_config.go @@ -0,0 +1,21 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gjson + +// SetSplitChar sets the separator char for hierarchical data access. +func (j *Json) SetSplitChar(char byte) { + j.mu.Lock() + j.c = char + j.mu.Unlock() +} + +// SetViolenceCheck enables/disables violence check for hierarchical data access. +func (j *Json) SetViolenceCheck(enabled bool) { + j.mu.Lock() + j.vc = enabled + j.mu.Unlock() +} diff --git a/g/encoding/gjson/gjson_api_encoding.go b/g/encoding/gjson/gjson_api_encoding.go new file mode 100644 index 000000000..101616b32 --- /dev/null +++ b/g/encoding/gjson/gjson_api_encoding.go @@ -0,0 +1,76 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gjson + +import ( + "encoding/json" + "github.com/gogf/gf/g/encoding/gtoml" + "github.com/gogf/gf/g/encoding/gxml" + "github.com/gogf/gf/g/encoding/gyaml" +) + +func (j *Json) ToXml(rootTag...string) ([]byte, error) { + return gxml.Encode(j.ToMap(), rootTag...) +} + +func (j *Json) ToXmlString(rootTag...string) (string, error) { + b, e := j.ToXml(rootTag...) + return string(b), e +} + +func (j *Json) ToXmlIndent(rootTag...string) ([]byte, error) { + return gxml.EncodeWithIndent(j.ToMap(), rootTag...) +} + +func (j *Json) ToXmlIndentString(rootTag...string) (string, error) { + b, e := j.ToXmlIndent(rootTag...) + return string(b), e +} + +func (j *Json) ToJson() ([]byte, error) { + j.mu.RLock() + defer j.mu.RUnlock() + return Encode(*(j.p)) +} + +func (j *Json) ToJsonString() (string, error) { + b, e := j.ToJson() + return string(b), e +} + +func (j *Json) ToJsonIndent() ([]byte, error) { + j.mu.RLock() + defer j.mu.RUnlock() + return json.MarshalIndent(*(j.p), "", "\t") +} + +func (j *Json) ToJsonIndentString() (string, error) { + b, e := j.ToJsonIndent() + return string(b), e +} + +func (j *Json) ToYaml() ([]byte, error) { + j.mu.RLock() + defer j.mu.RUnlock() + return gyaml.Encode(*(j.p)) +} + +func (j *Json) ToYamlString() (string, error) { + b, e := j.ToYaml() + return string(b), e +} + +func (j *Json) ToToml() ([]byte, error) { + j.mu.RLock() + defer j.mu.RUnlock() + return gtoml.Encode(*(j.p)) +} + +func (j *Json) ToTomlString() (string, error) { + b, e := j.ToToml() + return string(b), e +} diff --git a/g/encoding/gjson/gjson_api_new_load.go b/g/encoding/gjson/gjson_api_new_load.go new file mode 100644 index 000000000..72a0df0ec --- /dev/null +++ b/g/encoding/gjson/gjson_api_new_load.go @@ -0,0 +1,182 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package gjson provides convenient API for JSON/XML/YAML/TOML data handling. +package gjson + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "github.com/gogf/gf/g/encoding/gtoml" + "github.com/gogf/gf/g/encoding/gxml" + "github.com/gogf/gf/g/encoding/gyaml" + "github.com/gogf/gf/g/internal/rwmutex" + "github.com/gogf/gf/g/os/gfcache" + "github.com/gogf/gf/g/text/gregex" + "github.com/gogf/gf/g/util/gconv" + "reflect" +) + +// New creates a Json object with any variable type of , +// but should be a map or slice for data access reason, +// or it will make no sense. +// The param specifies whether using this Json object +// in un-concurrent-safe context, which is false in default. +func New(data interface{}, unsafe...bool) *Json { + j := (*Json)(nil) + switch data.(type) { + case string, []byte: + if r, err := LoadContent(gconv.Bytes(data)); err == nil { + j = r + } else { + j = &Json { + p : &data, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : false , + } + } + default: + rv := reflect.ValueOf(data) + kind := rv.Kind() + switch kind { + case reflect.Slice: fallthrough + case reflect.Array: + i := interface{}(nil) + i = gconv.Interfaces(data) + j = &Json { + p : &i, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : false , + } + case reflect.Map: fallthrough + case reflect.Struct: + i := interface{}(nil) + i = gconv.Map(data) + j = &Json { + p : &i, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : false , + } + default: + j = &Json { + p : &data, + c : byte(gDEFAULT_SPLIT_CHAR), + vc : false , + } + } + } + j.mu = rwmutex.New(unsafe...) + return j +} + +// NewUnsafe creates a un-concurrent-safe Json object. +func NewUnsafe(data...interface{}) *Json { + if len(data) > 0 { + return New(data[0], true) + } + return New(nil, true) +} + +// Valid checks whether is a valid JSON data type. +func Valid(data interface{}) bool { + return json.Valid(gconv.Bytes(data)) +} + +// Encode encodes to JSON data type of bytes. +func Encode(value interface{}) ([]byte, error) { + return json.Marshal(value) +} + +// Decode decodes (string/[]byte) to golang variable. +func Decode(data interface{}) (interface{}, error) { + var value interface{} + if err := DecodeTo(gconv.Bytes(data), &value); err != nil { + return nil, err + } else { + return value, nil + } +} + +// Decode decodes (string/[]byte) to specified golang variable . +// The should be a pointer type. +func DecodeTo(data interface{}, v interface{}) error { + decoder := json.NewDecoder(bytes.NewReader(gconv.Bytes(data))) + decoder.UseNumber() + return decoder.Decode(v) +} + +// DecodeToJson codes (string/[]byte) to a Json object. +func DecodeToJson(data interface{}, unsafe...bool) (*Json, error) { + if v, err := Decode(gconv.Bytes(data)); err != nil { + return nil, err + } else { + return New(v, unsafe...), nil + } +} + +// Load loads content from specified file , +// and creates a Json object from its content. +func Load(path string, unsafe...bool) (*Json, error) { + return LoadContent(gfcache.GetBinContents(path), unsafe...) +} + +// LoadContent creates a Json object from given content, +// it checks the data type of automatically, +// supporting JSON, XML, YAML and TOML types of data. +func LoadContent(data interface{}, unsafe...bool) (*Json, error) { + var err error + var result interface{} + b := gconv.Bytes(data) + t := "json" + // auto check data type + if json.Valid(b) { + t = "json" + } else if gregex.IsMatch(`^<.+>.*$`, b) { + t = "xml" + } else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) { + t = "yml" + } else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) { + t = "toml" + } else { + return nil, errors.New("unsupported data type") + } + // convert to json type data + switch t { + case "json", ".json": + // ok + case "xml", ".xml": + // TODO UseNumber + b, err = gxml.ToJson(b) + + case "yml", "yaml", ".yml", ".yaml": + // TODO UseNumber + b, err = gyaml.ToJson(b) + + case "toml", ".toml": + // TODO UseNumber + b, err = gtoml.ToJson(b) + + default: + err = errors.New("nonsupport type " + t) + } + if err != nil { + return nil, err + } + if result == nil { + decoder := json.NewDecoder(bytes.NewReader(b)) + decoder.UseNumber() + if err := decoder.Decode(&result); err != nil { + return nil, err + } + switch result.(type) { + case string, []byte: + return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b)) + } + } + return New(result, unsafe...), nil +} diff --git a/g/encoding/gjson/gjson_func.go b/g/encoding/gjson/gjson_func.go new file mode 100644 index 000000000..ac4b9bcf3 --- /dev/null +++ b/g/encoding/gjson/gjson_func.go @@ -0,0 +1,63 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gjson + +//func MarshalOrdered(value interface{}) ([]byte, error) { +// buffer := bytes.NewBuffer(nil) +// rv := reflect.ValueOf(value) +// kind := rv.Kind() +// if kind == reflect.Ptr { +// rv = rv.Elem() +// kind = rv.Kind() +// } +// switch kind { +// case reflect.Slice: fallthrough +// case reflect.Array: +// buffer.WriteByte('[') +// length := rv.Len() +// for i := 0; i < length; i++ { +// if p, err := MarshalOrdered(rv.Index(i).Interface()); err != nil { +// return nil, err +// } else { +// buffer.Write(p) +// if i < length - 1 { +// buffer.WriteByte(',') +// } +// } +// } +// buffer.WriteByte(']') +// case reflect.Map: fallthrough +// case reflect.Struct: +// m := gconv.Map(value, "json") +// keys := make([]string, len(m)) +// index := 0 +// for key := range m { +// keys[index] = key +// index++ +// } +// sort.Strings(keys) +// buffer.WriteByte('{') +// for i, key := range keys { +// if p, err := MarshalOrdered(m[key]); err != nil { +// return nil, err +// } else { +// buffer.WriteString(fmt.Sprintf(`"%s":%s`, key, string(p))) +// if i < index - 1 { +// buffer.WriteByte(',') +// } +// } +// } +// buffer.WriteByte('}') +// default: +// if p, err := json.Marshal(value); err != nil { +// return nil, err +// } else { +// buffer.Write(p) +// } +// } +// return buffer.Bytes(), nil +//} \ No newline at end of file diff --git a/g/encoding/gjson/gjson_bench_test.go b/g/encoding/gjson/gjson_z_bench_test.go similarity index 100% rename from g/encoding/gjson/gjson_bench_test.go rename to g/encoding/gjson/gjson_z_bench_test.go diff --git a/g/encoding/gjson/gjson_unit_basic_test.go b/g/encoding/gjson/gjson_z_unit_basic_test.go similarity index 90% rename from g/encoding/gjson/gjson_unit_basic_test.go rename to g/encoding/gjson/gjson_z_unit_basic_test.go index 102a2e15b..b1d3430f1 100644 --- a/g/encoding/gjson/gjson_unit_basic_test.go +++ b/g/encoding/gjson/gjson_z_unit_basic_test.go @@ -225,29 +225,42 @@ func Test_Len(t *testing.T) { } func Test_Append(t *testing.T) { - gtest.Case(t, func() { - p := gjson.New(nil) - p.Append("a", 1) - p.Append("a", 2) - gtest.Assert(p.Get("a"), g.Slice{1, 2}) - }) - gtest.Case(t, func() { - p := gjson.New(nil) - p.Append("a.b", 1) - p.Append("a.c", 2) - gtest.Assert(p.Get("a"), g.Map{ - "b" : g.Slice{1}, - "c" : g.Slice{2}, - }) - }) - gtest.Case(t, func() { - p := gjson.New(nil) - p.Set("a", 1) - err := p.Append("a", 2) - gtest.AssertNE(err, nil) - gtest.Assert(p.Get("a"), 1) - }) + gtest.Case(t, func() { + p := gjson.New(nil) + p.Append("a", 1) + p.Append("a", 2) + gtest.Assert(p.Get("a"), g.Slice{1, 2}) + }) + gtest.Case(t, func() { + p := gjson.New(nil) + p.Append("a.b", 1) + p.Append("a.c", 2) + gtest.Assert(p.Get("a"), g.Map{ + "b" : g.Slice{1}, + "c" : g.Slice{2}, + }) + }) + gtest.Case(t, func() { + p := gjson.New(nil) + p.Set("a", 1) + err := p.Append("a", 2) + gtest.AssertNE(err, nil) + gtest.Assert(p.Get("a"), 1) + }) } - +func TestJson_ToJson(t *testing.T) { + gtest.Case(t, func() { + p := gjson.New("1") + s, e := p.ToJsonString() + gtest.Assert(e, nil) + gtest.Assert(s, "1") + }) + gtest.Case(t, func() { + p := gjson.New("a") + s, e := p.ToJsonString() + gtest.Assert(e, nil) + gtest.Assert(s, `"a"`) + }) +} diff --git a/g/encoding/gjson/gjson_unit_load_test.go b/g/encoding/gjson/gjson_z_unit_load_test.go similarity index 100% rename from g/encoding/gjson/gjson_unit_load_test.go rename to g/encoding/gjson/gjson_z_unit_load_test.go diff --git a/g/encoding/gjson/gjson_unit_set_test.go b/g/encoding/gjson/gjson_z_unit_set_test.go similarity index 100% rename from g/encoding/gjson/gjson_unit_set_test.go rename to g/encoding/gjson/gjson_z_unit_set_test.go diff --git a/g/encoding/gparser/gparser.go b/g/encoding/gparser/gparser.go index 2e388d7ce..56c2129e3 100644 --- a/g/encoding/gparser/gparser.go +++ b/g/encoding/gparser/gparser.go @@ -8,283 +8,10 @@ package gparser import ( - "github.com/gogf/gf/g/encoding/gjson" - "time" + "github.com/gogf/gf/g/encoding/gjson" ) type Parser struct { json *gjson.Json } -// New creates a Parser object with any variable type of , -// but should be a map or slice for data access reason, -// or it will make no sense. -// The param specifies whether using this Parser object -// in un-concurrent-safe context, which is false in default. -func New(value interface{}, unsafe...bool) *Parser { - return &Parser{gjson.New(value, unsafe...)} -} - -// NewUnsafe creates a un-concurrent-safe Parser object. -func NewUnsafe (value...interface{}) *Parser { - if len(value) > 0 { - return &Parser{gjson.New(value[0], false)} - } - return &Parser{gjson.New(nil, false)} -} - -// Load loads content from specified file , -// and creates a Parser object from its content. -func Load (path string, unsafe...bool) (*Parser, error) { - if j, e := gjson.Load(path, unsafe...); e == nil { - return &Parser{j}, nil - } else { - return nil, e - } -} - -// LoadContent creates a Parser object from given content, -// it checks the data type of automatically, -// supporting JSON, XML, YAML and TOML types of data. -func LoadContent (data []byte, unsafe...bool) (*Parser, error) { - if j, e := gjson.LoadContent(data, unsafe...); e == nil { - return &Parser{j}, nil - } else { - return nil, e - } -} - -// SetSplitChar sets the separator char for hierarchical data access. -func (p *Parser) SetSplitChar(char byte) { - p.json.SetSplitChar(char) -} - -// SetViolenceCheck enables/disables violence check for hierarchical data access. -func (p *Parser) SetViolenceCheck(check bool) { - p.json.SetViolenceCheck(check) -} - -// GetToVar gets the value by specified , -// and converts it to specified golang variable . -// The should be a pointer type. -func (p *Parser) GetToVar(pattern string, v interface{}) error { - return p.json.GetToVar(pattern, v) -} - -// GetMap gets the value by specified , -// and converts it to map[string]interface{}. -func (p *Parser) GetMap(pattern string) map[string]interface{} { - return p.json.GetMap(pattern) -} - -// GetArray gets the value by specified , -// and converts it to a slice of []interface{}. -func (p *Parser) GetArray(pattern string) []interface{} { - return p.json.GetArray(pattern) -} - -// GetString gets the value by specified , -// and converts it to string. -func (p *Parser) GetString(pattern string) string { - return p.json.GetString(pattern) -} - -// GetStrings gets the value by specified , -// and converts it to a slice of []string. -func (p *Parser) GetStrings(pattern string) []string { - return p.json.GetStrings(pattern) -} - -func (p *Parser) GetInterfaces(pattern string) []interface{} { - return p.json.GetInterfaces(pattern) -} - -func (p *Parser) GetTime(pattern string, format ... string) time.Time { - return p.json.GetTime(pattern, format...) -} - -func (p *Parser) GetTimeDuration(pattern string) time.Duration { - return p.json.GetTimeDuration(pattern) -} - -// GetBool gets the value by specified , -// and converts it to bool. -// It returns false when value is: "", 0, false, off, nil; -// or returns true instead. -func (p *Parser) GetBool(pattern string) bool { - return p.json.GetBool(pattern) -} - -func (p *Parser) GetInt(pattern string) int { - return p.json.GetInt(pattern) -} - -func (p *Parser) GetInt8(pattern string) int8 { - return p.json.GetInt8(pattern) -} - -func (p *Parser) GetInt16(pattern string) int16 { - return p.json.GetInt16(pattern) -} - -func (p *Parser) GetInt32(pattern string) int32 { - return p.json.GetInt32(pattern) -} - -func (p *Parser) GetInt64(pattern string) int64 { - return p.json.GetInt64(pattern) -} - -func (p *Parser) GetInts(pattern string) []int { - return p.json.GetInts(pattern) -} - -func (p *Parser) GetUint(pattern string) uint { - return p.json.GetUint(pattern) -} - -func (p *Parser) GetUint8(pattern string) uint8 { - return p.json.GetUint8(pattern) -} - -func (p *Parser) GetUint16(pattern string) uint16 { - return p.json.GetUint16(pattern) -} - -func (p *Parser) GetUint32(pattern string) uint32 { - return p.json.GetUint32(pattern) -} - -func (p *Parser) GetUint64(pattern string) uint64 { - return p.json.GetUint64(pattern) -} - -func (p *Parser) GetFloat32(pattern string) float32 { - return p.json.GetFloat32(pattern) -} - -func (p *Parser) GetFloat64(pattern string) float64 { - return p.json.GetFloat64(pattern) -} - -func (p *Parser) GetFloats(pattern string) []float64 { - return p.json.GetFloats(pattern) -} - -// GetToStruct gets the value by specified , -// and converts it to specified object . -// The should be the pointer to an object. -func (p *Parser) GetToStruct(pattern string, objPointer interface{}) error { - return p.json.GetToStruct(pattern, objPointer) -} - -// Set sets value with specified . -// It supports hierarchical data access by char separator, which is '.' in default. -func (p *Parser) Set(pattern string, value interface{}) error { - return p.json.Set(pattern, value) -} - -// Len returns the length/size of the value by specified . -// The target value by should be type of slice or map. -// It returns -1 if the target value is not found, or its type is invalid. -func (p *Parser) Len(pattern string) int { - return p.json.Len(pattern) -} - -// Append appends value to the value by specified . -// The target value by should be type of slice. -func (p *Parser) Append(pattern string, value interface{}) error { - return p.json.Append(pattern, value) -} - -// Remove deletes value with specified . -// It supports hierarchical data access by char separator, which is '.' in default. -func (p *Parser) Remove(pattern string) error { - return p.json.Remove(pattern) -} - -// Get returns value by specified . -// It returns all values of current Json object, if is empty or not specified. -// It returns nil if no value found by . -// -// We can also access slice item by its index number in , -// eg: "items.name.first", "list.10". -func (p *Parser) Get(pattern...string) interface{} { - return p.json.Get(pattern...) -} - -// ToMap converts current object values to map[string]interface{}. -// It returns nil if fails. -func (p *Parser) ToMap() map[string]interface{} { - return p.json.ToMap() -} - -// ToArray converts current object values to []interface{}. -// It returns nil if fails. -func (p *Parser) ToArray() []interface{} { - return p.json.ToArray() -} - -func (p *Parser) ToXml(rootTag...string) ([]byte, error) { - return p.json.ToXml(rootTag...) -} - -func (p *Parser) ToXmlIndent(rootTag...string) ([]byte, error) { - return p.json.ToXmlIndent(rootTag...) -} - -func (p *Parser) ToJson() ([]byte, error) { - return p.json.ToJson() -} - -func (p *Parser) ToJsonIndent() ([]byte, error) { - return p.json.ToJsonIndent() -} - -func (p *Parser) ToYaml() ([]byte, error) { - return p.json.ToYaml() -} - -func (p *Parser) ToToml() ([]byte, error) { - return p.json.ToToml() -} - -// Dump prints current Json object with more manually readable. -func (p *Parser) Dump() error { - return p.json.Dump() -} - -// ToStruct converts current Json object to specified object. -// The should be a pointer type. -func (p *Parser) ToStruct(o interface{}) error { - return p.json.ToStruct(o) -} - -func VarToXml(value interface{}, rootTag...string) ([]byte, error) { - return New(value).ToXml(rootTag...) -} - -func VarToXmlIndent(value interface{}, rootTag...string) ([]byte, error) { - return New(value).ToXmlIndent(rootTag...) -} - -func VarToJson(value interface{}) ([]byte, error) { - return New(value).ToJson() -} - -func VarToJsonIndent(value interface{}) ([]byte, error) { - return New(value).ToJsonIndent() -} - -func VarToYaml(value interface{}) ([]byte, error) { - return New(value).ToYaml() -} - -func VarToToml(value interface{}) ([]byte, error) { - return New(value).ToToml() -} - -func VarToStruct(value interface{}, obj interface{}) error { - return New(value).ToStruct(obj) -} - diff --git a/g/encoding/gparser/gparser_api.go b/g/encoding/gparser/gparser_api.go new file mode 100644 index 000000000..f579dff94 --- /dev/null +++ b/g/encoding/gparser/gparser_api.go @@ -0,0 +1,201 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gp. + +package gparser + +import ( + "github.com/gogf/gf/g/container/gvar" + "github.com/gogf/gf/g/os/gtime" + "time" +) + +// Val returns the value. +func (p *Parser) Value() interface{} { + return p.json.Value() +} + +// Get returns value by specified . +// It returns all values of current Json object, if is empty or not specified. +// It returns nil if no value found by . +// +// We can also access slice item by its index number in , +// eg: "items.name.first", "list.10". +// +// It returns a default value specified by if value for is not found. +func (p *Parser) Get(pattern string, def...interface{}) interface{} { + return p.json.Get(pattern, def...) +} + +// GetVar returns a *gvar.Var with value by given . +func (p *Parser) GetVar(pattern string, def...interface{}) *gvar.Var { + return p.json.GetVar(pattern, def...) +} + +// GetMap gets the value by specified , +// and converts it to map[string]interface{}. +func (p *Parser) GetMap(pattern string, def...interface{}) map[string]interface{} { + return p.json.GetMap(pattern, def...) +} + +// GetArray gets the value by specified , +// and converts it to a slice of []interface{}. +func (p *Parser) GetArray(pattern string, def...interface{}) []interface{} { + return p.json.GetArray(pattern, def...) +} + +// GetString gets the value by specified , +// and converts it to string. +func (p *Parser) GetString(pattern string, def...interface{}) string { + return p.json.GetString(pattern, def...) +} + +// GetBool gets the value by specified , +// and converts it to bool. +// It returns false when value is: "", 0, false, off, nil; +// or returns true instead. +func (p *Parser) GetBool(pattern string, def...interface{}) bool { + return p.json.GetBool(pattern, def...) +} + +func (p *Parser) GetInt(pattern string, def...interface{}) int { + return p.json.GetInt(pattern, def...) +} + +func (p *Parser) GetInt8(pattern string, def...interface{}) int8 { + return p.json.GetInt8(pattern, def...) +} + +func (p *Parser) GetInt16(pattern string, def...interface{}) int16 { + return p.json.GetInt16(pattern, def...) +} + +func (p *Parser) GetInt32(pattern string, def...interface{}) int32 { + return p.json.GetInt32(pattern, def...) +} + +func (p *Parser) GetInt64(pattern string, def...interface{}) int64 { + return p.json.GetInt64(pattern, def...) +} + +func (p *Parser) GetInts(pattern string, def...interface{}) []int { + return p.json.GetInts(pattern, def...) +} + +func (p *Parser) GetUint(pattern string, def...interface{}) uint { + return p.json.GetUint(pattern, def...) +} + +func (p *Parser) GetUint8(pattern string, def...interface{}) uint8 { + return p.json.GetUint8(pattern, def...) +} + +func (p *Parser) GetUint16(pattern string, def...interface{}) uint16 { + return p.json.GetUint16(pattern, def...) +} + +func (p *Parser) GetUint32(pattern string, def...interface{}) uint32 { + return p.json.GetUint32(pattern, def...) +} + +func (p *Parser) GetUint64(pattern string, def...interface{}) uint64 { + return p.json.GetUint64(pattern, def...) +} + +func (p *Parser) GetFloat32(pattern string, def...interface{}) float32 { + return p.json.GetFloat32(pattern, def...) +} + +func (p *Parser) GetFloat64(pattern string, def...interface{}) float64 { + return p.json.GetFloat64(pattern, def...) +} + +func (p *Parser) GetFloats(pattern string, def...interface{}) []float64 { + return p.json.GetFloats(pattern, def...) +} + +// GetStrings gets the value by specified , +// and converts it to a slice of []string. +func (p *Parser) GetStrings(pattern string, def...interface{}) []string { + return p.json.GetStrings(pattern, def...) +} + +func (p *Parser) GetInterfaces(pattern string, def...interface{}) []interface{} { + return p.json.GetInterfaces(pattern, def...) +} + +func (p *Parser) GetTime(pattern string, format...string) time.Time { + return p.json.GetTime(pattern, format...) +} + +func (p *Parser) GetDuration(pattern string, def...interface{}) time.Duration { + return p.json.GetDuration(pattern, def...) +} + +func (p *Parser) GetGTime(pattern string, format...string) *gtime.Time { + return p.json.GetGTime(pattern, format...) +} + +// GetToVar gets the value by specified , +// and converts it to specified golang variable . +// The should be a pointer type. +func (p *Parser) GetToVar(pattern string, pointer interface{}) error { + return p.json.GetToVar(pattern, pointer) +} + +// GetToStruct gets the value by specified , +// and converts it to specified object . +// The should be the pointer to a struct. +func (p *Parser) GetToStruct(pattern string, pointer interface{}) error { + return p.json.GetToStruct(pattern, pointer) +} + +// Set sets value with specified . +// It supports hierarchical data access by char separator, which is '.' in default. +func (p *Parser) Set(pattern string, value interface{}) error { + return p.json.Set(pattern, value) +} + +// Len returns the length/size of the value by specified . +// The target value by should be type of slice or map. +// It returns -1 if the target value is not found, or its type is invalid. +func (p *Parser) Len(pattern string) int { + return p.json.Len(pattern) +} + +// Append appends value to the value by specified . +// The target value by should be type of slice. +func (p *Parser) Append(pattern string, value interface{}) error { + return p.json.Append(pattern, value) +} + +// Remove deletes value with specified . +// It supports hierarchical data access by char separator, which is '.' in default. +func (p *Parser) Remove(pattern string) error { + return p.json.Remove(pattern) +} + +// ToMap converts current object values to map[string]interface{}. +// It returns nil if fails. +func (p *Parser) ToMap() map[string]interface{} { + return p.json.ToMap() +} + +// ToArray converts current object values to []interface{}. +// It returns nil if fails. +func (p *Parser) ToArray() []interface{} { + return p.json.ToArray() +} + +// ToStruct converts current Json object to specified object. +// The should be a pointer type. +func (p *Parser) ToStruct(pointer interface{}) error { + return p.json.ToStruct(pointer) +} + +// Dump prints current Json object with more manually readable. +func (p *Parser) Dump() error { + return p.json.Dump() +} \ No newline at end of file diff --git a/g/encoding/gparser/gparser_api_config.go b/g/encoding/gparser/gparser_api_config.go new file mode 100644 index 000000000..644b5aa4b --- /dev/null +++ b/g/encoding/gparser/gparser_api_config.go @@ -0,0 +1,17 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gp. + +package gparser + +// SetSplitChar sets the separator char for hierarchical data access. +func (p *Parser) SetSplitChar(char byte) { + p.json.SetSplitChar(char) +} + +// SetViolenceCheck enables/disables violence check for hierarchical data access. +func (p *Parser) SetViolenceCheck(check bool) { + p.json.SetViolenceCheck(check) +} \ No newline at end of file diff --git a/g/encoding/gparser/gparser_api_encoding.go b/g/encoding/gparser/gparser_api_encoding.go new file mode 100644 index 000000000..c9e17f252 --- /dev/null +++ b/g/encoding/gparser/gparser_api_encoding.go @@ -0,0 +1,76 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gp. + +package gparser + +func (p *Parser) ToXml(rootTag...string) ([]byte, error) { + return p.json.ToXml(rootTag...) +} + +func (p *Parser) ToXmlIndent(rootTag...string) ([]byte, error) { + return p.json.ToXmlIndent(rootTag...) +} + +func (p *Parser) ToJson() ([]byte, error) { + return p.json.ToJson() +} + +func (p *Parser) ToJsonString() (string, error) { + return p.json.ToJsonString() +} + +func (p *Parser) ToJsonIndent() ([]byte, error) { + return p.json.ToJsonIndent() +} + +func (p *Parser) ToJsonIndentString() (string, error) { + return p.json.ToJsonIndentString() +} + +func (p *Parser) ToYaml() ([]byte, error) { + return p.json.ToYaml() +} + +func (p *Parser) ToToml() ([]byte, error) { + return p.json.ToToml() +} + +func VarToXml(value interface{}, rootTag...string) ([]byte, error) { + return New(value).ToXml(rootTag...) +} + +func VarToXmlIndent(value interface{}, rootTag...string) ([]byte, error) { + return New(value).ToXmlIndent(rootTag...) +} + +func VarToJson(value interface{}) ([]byte, error) { + return New(value).ToJson() +} + +func VarToJsonString(value interface{}) (string, error) { + return New(value).ToJsonString() +} + +func VarToJsonIndent(value interface{}) ([]byte, error) { + return New(value).ToJsonIndent() +} + +func VarToJsonIndentString(value interface{}) (string, error) { + return New(value).ToJsonIndentString() +} + +func VarToYaml(value interface{}) ([]byte, error) { + return New(value).ToYaml() +} + +func VarToToml(value interface{}) ([]byte, error) { + return New(value).ToToml() +} + +func VarToStruct(value interface{}, obj interface{}) error { + return New(value).ToStruct(obj) +} + diff --git a/g/encoding/gparser/gparser_api_new_load.go b/g/encoding/gparser/gparser_api_new_load.go new file mode 100644 index 000000000..434d05bd1 --- /dev/null +++ b/g/encoding/gparser/gparser_api_new_load.go @@ -0,0 +1,49 @@ +// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://gitee.com/johng/gp. + +package gparser + +import ( + "github.com/gogf/gf/g/encoding/gjson" +) + +// New creates a Parser object with any variable type of , +// but should be a map or slice for data access reason, +// or it will make no sense. +// The param specifies whether using this Parser object +// in un-concurrent-safe context, which is false in default. +func New(value interface{}, unsafe...bool) *Parser { + return &Parser{gjson.New(value, unsafe...)} +} + +// NewUnsafe creates a un-concurrent-safe Parser object. +func NewUnsafe(value...interface{}) *Parser { + if len(value) > 0 { + return &Parser{gjson.New(value[0], false)} + } + return &Parser{gjson.New(nil, false)} +} + +// Load loads content from specified file , +// and creates a Parser object from its content. +func Load(path string, unsafe...bool) (*Parser, error) { + if j, e := gjson.Load(path, unsafe...); e == nil { + return &Parser{j}, nil + } else { + return nil, e + } +} + +// LoadContent creates a Parser object from given content, +// it checks the data type of automatically, +// supporting JSON, XML, YAML and TOML types of data. +func LoadContent(data []byte, unsafe...bool) (*Parser, error) { + if j, e := gjson.LoadContent(data, unsafe...); e == nil { + return &Parser{j}, nil + } else { + return nil, e + } +} diff --git a/g/frame/gins/gins.go b/g/frame/gins/gins.go index 1c936adfe..0794b6367 100644 --- a/g/frame/gins/gins.go +++ b/g/frame/gins/gins.go @@ -28,7 +28,7 @@ const ( ) // 单例对象存储器 -var instances = gmap.NewStringInterfaceMap() +var instances = gmap.NewStrAnyMap() // 获取单例对象 func Get(key string) interface{} { @@ -201,10 +201,10 @@ func Redis(name...string) *gredis.Redis { redisConfig.MaxActive = gconv.Int(v) } if v, ok := parse["idleTimeout"]; ok { - redisConfig.IdleTimeout = gconv.TimeDuration(v)*time.Second + redisConfig.IdleTimeout = gconv.Duration(v)*time.Second } if v, ok := parse["maxConnLifetime"]; ok { - redisConfig.MaxConnLifetime = gconv.TimeDuration(v)*time.Second + redisConfig.MaxConnLifetime = gconv.Duration(v)*time.Second } addConfigMonitor(key, config) return gredis.New(redisConfig) @@ -225,7 +225,7 @@ func Redis(name...string) *gredis.Redis { glog.Errorfln(`configuration for redis not found for group "%s"`, group) } } else { - glog.Errorfln(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.GetFilePath()) + glog.Errorfln(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath()) } return nil }) @@ -238,7 +238,7 @@ func Redis(name...string) *gredis.Redis { // 添加对单例对象的配置文件inotify监控 func addConfigMonitor(key string, config *gcfg.Config) { // 使用gfsnotify进行文件监控,当配置文件有任何变化时,清空对象单例缓存 - if path := config.GetFilePath(); path != "" { + if path := config.FilePath(); path != "" { gfsnotify.Add(path, func(event *gfsnotify.Event) { instances.Remove(key) }) @@ -246,7 +246,7 @@ func addConfigMonitor(key string, config *gcfg.Config) { } // 模板内置方法:config -func funcConfig(pattern string, file...string) string { +func funcConfig(pattern string, file...interface{}) string { return Config().GetString(pattern, file...) } diff --git a/g/internal/cmdenv/cmdenv.go b/g/internal/cmdenv/cmdenv.go index 9dd9a8d08..067310142 100644 --- a/g/internal/cmdenv/cmdenv.go +++ b/g/internal/cmdenv/cmdenv.go @@ -32,7 +32,7 @@ func init() { // 规则: // 1、命令行参数以小写字母格式,使用: gf.包名.变量名 传递; // 2、环境变量参数以大写字母格式,使用: GF_包名_变量名 传递; -func Get(key string, def...interface{}) gvar.VarRead { +func Get(key string, def...interface{}) *gvar.Var { value := interface{}(nil) if len(def) > 0 { value = def[0] diff --git a/g/internal/rwmutex/rwmutex.go b/g/internal/rwmutex/rwmutex.go index 98754e5e1..62f933192 100644 --- a/g/internal/rwmutex/rwmutex.go +++ b/g/internal/rwmutex/rwmutex.go @@ -8,7 +8,6 @@ package rwmutex import "sync" -// RWMutex的封装,支持对并发安全开启/关闭的控制。 type RWMutex struct { sync.RWMutex safe bool @@ -28,26 +27,26 @@ func (mu *RWMutex) IsSafe() bool { return mu.safe } -func (mu *RWMutex) Lock(force...bool) { - if mu.safe || (len(force) > 0 && force[0]) { +func (mu *RWMutex) Lock() { + if mu.safe { mu.RWMutex.Lock() } } -func (mu *RWMutex) Unlock(force...bool) { - if mu.safe || (len(force) > 0 && force[0]) { +func (mu *RWMutex) Unlock() { + if mu.safe { mu.RWMutex.Unlock() } } -func (mu *RWMutex) RLock(force...bool) { - if mu.safe || (len(force) > 0 && force[0]) { +func (mu *RWMutex) RLock() { + if mu.safe { mu.RWMutex.RLock() } } -func (mu *RWMutex) RUnlock(force...bool) { - if mu.safe || (len(force) > 0 && force[0]) { +func (mu *RWMutex) RUnlock() { + if mu.safe { mu.RWMutex.RUnlock() } } \ No newline at end of file diff --git a/g/net/ghttp/ghttp_request.go b/g/net/ghttp/ghttp_request.go index 9f8c314dc..e6473f4e4 100644 --- a/g/net/ghttp/ghttp_request.go +++ b/g/net/ghttp/ghttp_request.go @@ -71,12 +71,12 @@ func (r *Request) WebSocket() (*WebSocket, error) { // 获得指定名称的参数字符串(Router/GET/POST),同 GetRequestString // 这是常用方法的简化别名 -func (r *Request) Get(key string, def ... string) string { +func (r *Request) Get(key string, def...interface{}) string { return r.GetRequestString(key, def...) } // 建议都用该参数替代参数获取 -func (r *Request) GetVar(key string, def ... interface{}) gvar.VarRead { +func (r *Request) GetVar(key string, def...interface{}) *gvar.Var { return r.GetRequestVar(key, def...) } @@ -110,43 +110,43 @@ func (r *Request) GetJson() *gjson.Json { return nil } -func (r *Request) GetString(key string, def ... string) string { +func (r *Request) GetString(key string, def...interface{}) string { return r.GetRequestString(key, def...) } -func (r *Request) GetInt(key string, def ... int) int { +func (r *Request) GetInt(key string, def...interface{}) int { return r.GetRequestInt(key, def...) } -func (r *Request) GetInts(key string, def ... []int) []int { +func (r *Request) GetInts(key string, def...interface{}) []int { return r.GetRequestInts(key, def...) } -func (r *Request) GetUint(key string, def ... uint) uint { +func (r *Request) GetUint(key string, def...interface{}) uint { return r.GetRequestUint(key, def...) } -func (r *Request) GetFloat32(key string, def ... float32) float32 { +func (r *Request) GetFloat32(key string, def...interface{}) float32 { return r.GetRequestFloat32(key, def...) } -func (r *Request) GetFloat64(key string, def ... float64) float64 { +func (r *Request) GetFloat64(key string, def...interface{}) float64 { return r.GetRequestFloat64(key, def...) } -func (r *Request) GetFloats(key string, def ... []float64) []float64 { +func (r *Request) GetFloats(key string, def...interface{}) []float64 { return r.GetRequestFloats(key, def...) } -func (r *Request) GetArray(key string, def ... []string) []string { +func (r *Request) GetArray(key string, def...interface{}) []string { return r.GetRequestArray(key, def...) } -func (r *Request) GetStrings(key string, def ... []string) []string { +func (r *Request) GetStrings(key string, def...interface{}) []string { return r.GetRequestStrings(key, def...) } -func (r *Request) GetInterfaces(key string, def ... []interface{}) []interface{} { +func (r *Request) GetInterfaces(key string, def...interface{}) []interface{} { return r.GetRequestInterfaces(key, def...) } @@ -154,9 +154,10 @@ func (r *Request) GetMap(def...map[string]string) map[string]string { return r.GetRequestMap(def...) } -// 将所有的request参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 -func (r *Request) GetToStruct(object interface{}, mapping...map[string]string) { - r.GetRequestToStruct(object, mapping...) +// 将所有的request参数映射到struct属性上,参数pointer应当为一个struct对象的指针, +// mapping为非必需参数,自定义参数与属性的映射关系 +func (r *Request) GetToStruct(pointer interface{}, mapping...map[string]string) { + r.GetRequestToStruct(pointer, mapping...) } // 仅退出当前逻辑执行函数, 如:服务函数、HOOK函数 @@ -233,9 +234,9 @@ func (r *Request) GetReferer() string { } // 获得结构体对象的参数名称标签,构成map返回 -func (r *Request) getStructParamsTagMap(object interface{}) map[string]string { +func (r *Request) getStructParamsTagMap(pointer interface{}) map[string]string { tagMap := make(map[string]string) - fields := structs.Fields(object) + fields := structs.Fields(pointer) for _, field := range fields { if tag := field.Tag("params"); tag != "" { for _, v := range strings.Split(tag, ",") { diff --git a/g/net/ghttp/ghttp_request_params.go b/g/net/ghttp/ghttp_request_params.go index e5d6268cc..2277c9cc3 100644 --- a/g/net/ghttp/ghttp_request_params.go +++ b/g/net/ghttp/ghttp_request_params.go @@ -17,12 +17,15 @@ func (r *Request) SetParam(key string, value interface{}) { } // 获取请求流程共享变量 -func (r *Request) GetParam(key string) gvar.VarRead { +func (r *Request) GetParam(key string, def...interface{}) *gvar.Var { if r.params != nil { if v, ok := r.params[key]; ok { return gvar.New(v, true) } } + if len(def) > 0 { + return gvar.New(def[0], true) + } return gvar.New(nil, true) } diff --git a/g/net/ghttp/ghttp_request_post.go b/g/net/ghttp/ghttp_request_post.go index 83e582ffe..f78c3bfbb 100644 --- a/g/net/ghttp/ghttp_request_post.go +++ b/g/net/ghttp/ghttp_request_post.go @@ -32,121 +32,94 @@ func (r *Request) AddPost(key string, value string) { } // 获得post参数 -func (r *Request) GetPost(key string, def...[]string) []string { +func (r *Request) GetPost(key string, def...interface{}) []string { r.initPost() if v, ok := r.PostForm[key]; ok { return v } if len(def) > 0 { - return def[0] + return gconv.Strings(def[0]) } return nil } -func (r *Request) GetPostString(key string, def ... string) string { - value := r.GetPost(key) +func (r *Request) GetPostString(key string, def...interface{}) string { + value := r.GetPost(key, def...) if value != nil && value[0] != "" { return value[0] } - if len(def) > 0 { - return def[0] - } return "" } -func (r *Request) GetPostBool(key string, def ... bool) bool { - value := r.GetPostString(key) +func (r *Request) GetPostBool(key string, def...interface{}) bool { + value := r.GetPostString(key, def...) if value != "" { return gconv.Bool(value) } - if len(def) > 0 { - return def[0] - } return false } -func (r *Request) GetPostInt(key string, def ... int) int { - value := r.GetPostString(key) +func (r *Request) GetPostInt(key string, def...interface{}) int { + value := r.GetPostString(key, def...) if value != "" { return gconv.Int(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetPostInts(key string, def ... []int) []int { - value := r.GetPost(key) +func (r *Request) GetPostInts(key string, def...interface{}) []int { + value := r.GetPost(key, def...) if value != nil { return gconv.Ints(value) } - if len(def) > 0 { - return def[0] - } return nil } -func (r *Request) GetPostUint(key string, def ... uint) uint { - value := r.GetPostString(key) +func (r *Request) GetPostUint(key string, def...interface{}) uint { + value := r.GetPostString(key, def...) if value != "" { return gconv.Uint(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetPostFloat32(key string, def ... float32) float32 { - value := r.GetPostString(key) +func (r *Request) GetPostFloat32(key string, def...interface{}) float32 { + value := r.GetPostString(key, def...) if value != "" { return gconv.Float32(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetPostFloat64(key string, def ... float64) float64 { - value := r.GetPostString(key) +func (r *Request) GetPostFloat64(key string, def...interface{}) float64 { + value := r.GetPostString(key, def...) if value != "" { return gconv.Float64(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetPostFloats(key string, def ... []float64) []float64 { - value := r.GetPost(key) +func (r *Request) GetPostFloats(key string, def...interface{}) []float64 { + value := r.GetPost(key, def...) if value != nil { return gconv.Floats(value) } - if len(def) > 0 { - return def[0] - } return nil } -func (r *Request) GetPostArray(key string, def ... []string) []string { +func (r *Request) GetPostArray(key string, def...interface{}) []string { return r.GetPost(key, def...) } -func (r *Request) GetPostStrings(key string, def ... []string) []string { +func (r *Request) GetPostStrings(key string, def...interface{}) []string { return r.GetPost(key, def...) } -func (r *Request) GetPostInterfaces(key string, def ... []interface{}) []interface{} { - value := r.GetPost(key) +func (r *Request) GetPostInterfaces(key string, def...interface{}) []interface{} { + value := r.GetPost(key, def...) if value != nil { return gconv.Interfaces(value) } - if len(def) > 0 { - return def[0] - } return nil } diff --git a/g/net/ghttp/ghttp_request_query.go b/g/net/ghttp/ghttp_request_query.go index e39a224db..bfc437aef 100644 --- a/g/net/ghttp/ghttp_request_query.go +++ b/g/net/ghttp/ghttp_request_query.go @@ -41,126 +41,99 @@ func (r *Request) AddQuery(key string, value string) { } // 获得指定名称的get参数列表 -func (r *Request) GetQuery(key string, def ... []string) []string { +func (r *Request) GetQuery(key string, def...interface{}) []string { r.initGet() if v, ok := r.queryVars[key]; ok { return v } if len(def) > 0 { - return def[0] + return gconv.Strings(def[0]) } return nil } -func (r *Request) GetQueryString(key string, def ... string) string { - value := r.GetQuery(key) +func (r *Request) GetQueryString(key string, def...interface{}) string { + value := r.GetQuery(key, def...) if value != nil && value[0] != "" { return value[0] } - if len(def) > 0 { - return def[0] - } return "" } -func (r *Request) GetQueryBool(key string, def ... bool) bool { - value := r.GetQueryString(key) +func (r *Request) GetQueryBool(key string, def...interface{}) bool { + value := r.GetQueryString(key, def...) if value != "" { return gconv.Bool(value) } - if len(def) > 0 { - return def[0] - } return false } -func (r *Request) GetQueryInt(key string, def ... int) int { - value := r.GetQueryString(key) +func (r *Request) GetQueryInt(key string, def...interface{}) int { + value := r.GetQueryString(key, def...) if value != "" { return gconv.Int(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetQueryInts(key string, def ... []int) []int { - value := r.GetQuery(key) +func (r *Request) GetQueryInts(key string, def...interface{}) []int { + value := r.GetQuery(key, def...) if value != nil { return gconv.Ints(value) } - if len(def) > 0 { - return def[0] - } return nil } -func (r *Request) GetQueryUint(key string, def ... uint) uint { - value := r.GetQueryString(key) +func (r *Request) GetQueryUint(key string, def...interface{}) uint { + value := r.GetQueryString(key, def...) if value != "" { return gconv.Uint(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetQueryFloat32(key string, def ... float32) float32 { - value := r.GetQueryString(key) +func (r *Request) GetQueryFloat32(key string, def...interface{}) float32 { + value := r.GetQueryString(key, def...) if value != "" { return gconv.Float32(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetQueryFloat64(key string, def ... float64) float64 { - value := r.GetQueryString(key) +func (r *Request) GetQueryFloat64(key string, def...interface{}) float64 { + value := r.GetQueryString(key, def...) if value != "" { return gconv.Float64(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetQueryFloats(key string, def ... []float64) []float64 { - value := r.GetQuery(key) +func (r *Request) GetQueryFloats(key string, def...interface{}) []float64 { + value := r.GetQuery(key, def...) if value != nil { return gconv.Floats(value) } - if len(def) > 0 { - return def[0] - } return nil } -func (r *Request) GetQueryArray(key string, def ... []string) []string { +func (r *Request) GetQueryArray(key string, def...interface{}) []string { return r.GetQuery(key, def...) } -func (r *Request) GetQueryStrings(key string, def ... []string) []string { +func (r *Request) GetQueryStrings(key string, def...interface{}) []string { return r.GetQuery(key, def...) } -func (r *Request) GetQueryInterfaces(key string, def ... []interface{}) []interface{} { - value := r.GetQuery(key) +func (r *Request) GetQueryInterfaces(key string, def...interface{}) []interface{} { + value := r.GetQuery(key, def...) if value != nil { return gconv.Interfaces(value) } - if len(def) > 0 { - return def[0] - } return nil } // 获取指定键名的关联数组,并且给定当指定键名不存在时的默认值 -func (r *Request) GetQueryMap(def ... map[string]string) map[string]string { +func (r *Request) GetQueryMap(def... map[string]string) map[string]string { r.initGet() m := make(map[string]string) for k, v := range r.queryVars { diff --git a/g/net/ghttp/ghttp_request_request.go b/g/net/ghttp/ghttp_request_request.go index f73485085..f51cd4b79 100644 --- a/g/net/ghttp/ghttp_request_request.go +++ b/g/net/ghttp/ghttp_request_request.go @@ -12,7 +12,7 @@ import ( ) // 获得router、post或者get提交的参数,如果有同名参数,那么按照router->get->post优先级进行覆盖 -func (r *Request) GetRequest(key string, def ... []string) []string { +func (r *Request) GetRequest(key string, def...interface{}) []string { v := r.GetRouterArray(key) if v == nil { v = r.GetQuery(key) @@ -21,126 +21,96 @@ func (r *Request) GetRequest(key string, def ... []string) []string { v = r.GetPost(key) } if v == nil && len(def) > 0 { - return def[0] + return gconv.Strings(def[0]) } return v } -func (r *Request) GetRequestVar(key string, def ... interface{}) gvar.VarRead { - value := r.GetRequest(key) +func (r *Request) GetRequestVar(key string, def...interface{}) *gvar.Var { + value := r.GetRequest(key, def...) if value != nil { return gvar.New(value[0], true) } - if len(def) > 0 { - return gvar.New(def[0], true) - } return gvar.New(nil, true) } -func (r *Request) GetRequestString(key string, def ... string) string { - value := r.GetRequest(key) +func (r *Request) GetRequestString(key string, def...interface{}) string { + value := r.GetRequest(key, def...) if value != nil && value[0] != "" { return value[0] } - if len(def) > 0 { - return def[0] - } return "" } -func (r *Request) GetRequestBool(key string, def ... bool) bool { - value := r.GetRequestString(key) +func (r *Request) GetRequestBool(key string, def...interface{}) bool { + value := r.GetRequestString(key, def...) if value != "" { return gconv.Bool(value) } - if len(def) > 0 { - return def[0] - } return false } -func (r *Request) GetRequestInt(key string, def ... int) int { - value := r.GetRequestString(key) +func (r *Request) GetRequestInt(key string, def...interface{}) int { + value := r.GetRequestString(key, def...) if value != "" { return gconv.Int(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetRequestInts(key string, def ... []int) []int { - value := r.GetRequest(key) +func (r *Request) GetRequestInts(key string, def...interface{}) []int { + value := r.GetRequest(key, def...) if value != nil { return gconv.Ints(value) } - if len(def) > 0 { - return def[0] - } return nil } -func (r *Request) GetRequestUint(key string, def ... uint) uint { - value := r.GetRequestString(key) +func (r *Request) GetRequestUint(key string, def...interface{}) uint { + value := r.GetRequestString(key, def...) if value != "" { return gconv.Uint(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetRequestFloat32(key string, def ... float32) float32 { - value := r.GetRequestString(key) +func (r *Request) GetRequestFloat32(key string, def...interface{}) float32 { + value := r.GetRequestString(key, def...) if value != "" { return gconv.Float32(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetRequestFloat64(key string, def ... float64) float64 { - value := r.GetRequestString(key) +func (r *Request) GetRequestFloat64(key string, def...interface{}) float64 { + value := r.GetRequestString(key, def...) if value != "" { return gconv.Float64(value) } - if len(def) > 0 { - return def[0] - } return 0 } -func (r *Request) GetRequestFloats(key string, def ... []float64) []float64 { - value := r.GetRequest(key) +func (r *Request) GetRequestFloats(key string, def...interface{}) []float64 { + value := r.GetRequest(key, def...) if value != nil { return gconv.Floats(value) } - if len(def) > 0 { - return def[0] - } return nil } -func (r *Request) GetRequestArray(key string, def ... []string) []string { +func (r *Request) GetRequestArray(key string, def...interface{}) []string { return r.GetRequest(key, def...) } -func (r *Request) GetRequestStrings(key string, def ... []string) []string { +func (r *Request) GetRequestStrings(key string, def...interface{}) []string { return r.GetRequest(key, def...) } -func (r *Request) GetRequestInterfaces(key string, def ... []interface{}) []interface{} { - value := r.GetRequest(key) +func (r *Request) GetRequestInterfaces(key string, def...interface{}) []interface{} { + value := r.GetRequest(key, def...) if value != nil { return gconv.Interfaces(value) } - if len(def) > 0 { - return def[0] - } return nil } @@ -162,8 +132,8 @@ func (r *Request) GetRequestMap(def...map[string]string) map[string]string { } // 将所有的request参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系 -func (r *Request) GetRequestToStruct(object interface{}, mapping...map[string]string) error { - tagmap := r.getStructParamsTagMap(object) +func (r *Request) GetRequestToStruct(pointer interface{}, mapping...map[string]string) error { + tagmap := r.getStructParamsTagMap(pointer) if len(mapping) > 0 { for k, v := range mapping[0] { tagmap[k] = v @@ -178,6 +148,6 @@ func (r *Request) GetRequestToStruct(object interface{}, mapping...map[string]st params = j.ToMap() } } - return gconv.Struct(params, object, tagmap) + return gconv.Struct(params, pointer, tagmap) } diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 5b2f64aa7..13efff4da 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -132,7 +132,7 @@ var ( methodsMap = make(map[string]struct{}) // WebServer表,用以存储和检索名称与Server对象之间的关联关系 - serverMapping = gmap.NewStringInterfaceMap() + serverMapping = gmap.NewStrAnyMap() // 正常运行的WebServer数量,如果没有运行、失败或者全部退出,那么该值为0 serverRunning = gtype.NewInt() diff --git a/g/net/ghttp/ghttp_server_cookie.go b/g/net/ghttp/ghttp_server_cookie.go index 97689b620..592aae043 100644 --- a/g/net/ghttp/ghttp_server_cookie.go +++ b/g/net/ghttp/ghttp_server_cookie.go @@ -134,15 +134,16 @@ func (c *Cookie) SetSessionId(id string) { } // 查询cookie -func (c *Cookie) Get(key string) string { +func (c *Cookie) Get(key string, def...string) string { c.init() if r, ok := c.data[key]; ok { if r.expire >= 0 { return r.value - } else { - return "" } } + if len(def) > 0 { + return def[0] + } return "" } diff --git a/g/net/ghttp/ghttp_server_session.go b/g/net/ghttp/ghttp_server_session.go index 61e29973c..58e588be6 100644 --- a/g/net/ghttp/ghttp_server_session.go +++ b/g/net/ghttp/ghttp_server_session.go @@ -21,7 +21,7 @@ import ( // SESSION对象 type Session struct { id string // SessionId - data *gmap.StringInterfaceMap // Session数据 + data *gmap.StrAnyMap // Session数据 server *Server // 所属Server request *Request // 关联的请求 } @@ -51,13 +51,13 @@ func (s *Session) init() { data := s.server.sessions.Get(id) if data != nil { s.id = id - s.data = data.(*gmap.StringInterfaceMap) + s.data = data.(*gmap.StrAnyMap) return } } // 否则执行初始化创建 s.id = s.request.Cookie.MakeSessionId() - s.data = gmap.NewStringInterfaceMap() + s.data = gmap.NewStrAnyMap() s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()) } } @@ -83,16 +83,10 @@ func (s *Session) Set(key string, value interface{}) { s.data.Set(key, value) } -// 批量设置(BatchSet别名) +// 批量设置 func (s *Session) Sets(m map[string]interface{}) { s.init() - s.BatchSet(m) -} - -// 批量设置 -func (s *Session) BatchSet(m map[string]interface{}) { - s.init() - s.data.BatchSet(m) + s.data.Sets(m) } // 判断键名是否存在 @@ -105,17 +99,22 @@ func (s *Session) Contains (key string) bool { } // 获取SESSION变量 -func (s *Session) Get(key string) interface{} { +func (s *Session) Get(key string, def...interface{}) interface{} { if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { s.init() - return s.data.Get(key) + if v := s.data.Get(key); v != nil { + return v + } + } + if len(def) > 0 { + return def[0] } return nil } // 获取SESSION,建议都用该方法获取参数 -func (s *Session) GetVar(key string) gvar.VarRead { - return gvar.NewRead(s.Get(key), true) +func (s *Session) GetVar(key string, def...interface{}) *gvar.Var { + return gvar.New(s.Get(key, def...), true) } // 删除session @@ -141,80 +140,80 @@ func (s *Session) UpdateExpire() { } } -func (s *Session) GetString(key string) string { - return gconv.String(s.Get(key)) +func (s *Session) GetString(key string, def...interface{}) string { + return gconv.String(s.Get(key, def...)) } -func (s *Session) GetBool(key string) bool { - return gconv.Bool(s.Get(key)) +func (s *Session) GetBool(key string, def...interface{}) bool { + return gconv.Bool(s.Get(key, def...)) } -func (s *Session) GetInt(key string) int { - return gconv.Int(s.Get(key)) } - - -func (s *Session) GetInt8(key string) int8 { - return gconv.Int8(s.Get(key)) +func (s *Session) GetInt(key string, def...interface{}) int { + return gconv.Int(s.Get(key, def...)) } -func (s *Session) GetInt16(key string) int16 { - return gconv.Int16(s.Get(key)) +func (s *Session) GetInt8(key string, def...interface{}) int8 { + return gconv.Int8(s.Get(key, def...)) } -func (s *Session) GetInt32(key string) int32 { - return gconv.Int32(s.Get(key)) +func (s *Session) GetInt16(key string, def...interface{}) int16 { + return gconv.Int16(s.Get(key, def...)) } -func (s *Session) GetInt64(key string) int64 { - return gconv.Int64(s.Get(key)) +func (s *Session) GetInt32(key string, def...interface{}) int32 { + return gconv.Int32(s.Get(key, def...)) } -func (s *Session) GetUint(key string) uint { - return gconv.Uint(s.Get(key)) +func (s *Session) GetInt64(key string, def...interface{}) int64 { + return gconv.Int64(s.Get(key, def...)) } -func (s *Session) GetUint8(key string) uint8 { - return gconv.Uint8(s.Get(key)) +func (s *Session) GetUint(key string, def...interface{}) uint { + return gconv.Uint(s.Get(key, def...)) } -func (s *Session) GetUint16(key string) uint16 { - return gconv.Uint16(s.Get(key)) +func (s *Session) GetUint8(key string, def...interface{}) uint8 { + return gconv.Uint8(s.Get(key, def...)) } -func (s *Session) GetUint32(key string) uint32 { - return gconv.Uint32(s.Get(key)) +func (s *Session) GetUint16(key string, def...interface{}) uint16 { + return gconv.Uint16(s.Get(key, def...)) } -func (s *Session) GetUint64(key string) uint64 { - return gconv.Uint64(s.Get(key)) +func (s *Session) GetUint32(key string, def...interface{}) uint32 { + return gconv.Uint32(s.Get(key, def...)) } -func (s *Session) GetFloat32(key string) float32 { - return gconv.Float32(s.Get(key)) +func (s *Session) GetUint64(key string, def...interface{}) uint64 { + return gconv.Uint64(s.Get(key, def...)) } -func (s *Session) GetFloat64(key string) float64 { - return gconv.Float64(s.Get(key)) +func (s *Session) GetFloat32(key string, def...interface{}) float32 { + return gconv.Float32(s.Get(key, def...)) } -func (s *Session) GetBytes(key string) []byte { - return gconv.Bytes(s.Get(key)) +func (s *Session) GetFloat64(key string, def...interface{}) float64 { + return gconv.Float64(s.Get(key, def...)) } -func (s *Session) GetInts(key string) []int { - return gconv.Ints(s.Get(key)) +func (s *Session) GetBytes(key string, def...interface{}) []byte { + return gconv.Bytes(s.Get(key, def...)) } -func (s *Session) GetFloats(key string) []float64 { - return gconv.Floats(s.Get(key)) +func (s *Session) GetInts(key string, def...interface{}) []int { + return gconv.Ints(s.Get(key, def...)) } -func (s *Session) GetStrings(key string) []string { - return gconv.Strings(s.Get(key)) +func (s *Session) GetFloats(key string, def...interface{}) []float64 { + return gconv.Floats(s.Get(key, def...)) } -func (s *Session) GetInterfaces(key string) []interface{} { - return gconv.Interfaces(s.Get(key)) +func (s *Session) GetStrings(key string, def...interface{}) []string { + return gconv.Strings(s.Get(key, def...)) +} + +func (s *Session) GetInterfaces(key string, def...interface{}) []interface{} { + return gconv.Interfaces(s.Get(key, def...)) } func (s *Session) GetTime(key string, format...string) time.Time { @@ -225,13 +224,13 @@ func (s *Session) GetGTime(key string, format...string) *gtime.Time { return gconv.GTime(s.Get(key), format...) } -func (s *Session) GetTimeDuration(key string) time.Duration { - return gconv.TimeDuration(s.Get(key)) +func (s *Session) GetDuration(key string, def...interface{}) time.Duration { + return gconv.Duration(s.Get(key, def...)) } -// 将变量转换为对象,注意 objPointer 参数必须为struct指针 -func (s *Session) GetStruct(key string, objPointer interface{}, attrMapping...map[string]string) error { - return gconv.Struct(s.Get(key), objPointer, attrMapping...) +// 将变量转换为对象,注意 pointer 参数必须为struct指针 +func (s *Session) GetStruct(key string, pointer interface{}, mapping...map[string]string) error { + return gconv.Struct(s.Get(key), pointer, mapping...) } diff --git a/g/net/gtcp/gtcp_pool.go b/g/net/gtcp/gtcp_pool.go index 7d56d239a..fb739e2a8 100644 --- a/g/net/gtcp/gtcp_pool.go +++ b/g/net/gtcp/gtcp_pool.go @@ -29,7 +29,7 @@ const ( var ( // 连接池对象map,键名为地址端口,键值为对应的连接池对象 - pools = gmap.NewStringInterfaceMap() + pools = gmap.NewStrAnyMap() ) // 创建TCP链接池对象 diff --git a/g/net/gtcp/gtcp_server.go b/g/net/gtcp/gtcp_server.go index eb68cedda..7962d9240 100644 --- a/g/net/gtcp/gtcp_server.go +++ b/g/net/gtcp/gtcp_server.go @@ -26,7 +26,7 @@ type Server struct { } // Server表,用以存储和检索名称与Server对象之间的关联关系 -var serverMapping = gmap.NewStringInterfaceMap() +var serverMapping = gmap.NewStrAnyMap() // 获取/创建一个空配置的TCP Server // 单例模式,请保证name的唯一性 diff --git a/g/net/gudp/gudp_server.go b/g/net/gudp/gudp_server.go index 2749940b1..a14a8d932 100644 --- a/g/net/gudp/gudp_server.go +++ b/g/net/gudp/gudp_server.go @@ -26,7 +26,7 @@ type Server struct { } // Server表,用以存储和检索名称与Server对象之间的关联关系 -var serverMapping = gmap.NewStringInterfaceMap() +var serverMapping = gmap.NewStrAnyMap() // 获取/创建一个空配置的UDP Server // 单例模式,请保证name的唯一性 diff --git a/g/os/gcache/gcache.go b/g/os/gcache/gcache.go index d730e92f7..f24ccf268 100644 --- a/g/os/gcache/gcache.go +++ b/g/os/gcache/gcache.go @@ -5,9 +5,6 @@ // You can obtain one at https://github.com/gogf/gf. // Package gcache provides high performance and concurrent-safe in-memory cache for process. -// -// 缓存模块, -// 并发安全的单进程高速缓存. package gcache // 全局缓存管理对象 @@ -25,8 +22,8 @@ func SetIfNotExist(key interface{}, value interface{}, expire int) bool { } // (使用全局KV缓存对象)批量设置kv缓存键值对,过期时间单位为**毫秒** -func BatchSet(data map[interface{}]interface{}, expire int) { - cache.BatchSet(data, expire) +func Sets(data map[interface{}]interface{}, expire int) { + cache.Sets(data, expire) } // (使用全局KV缓存对象)获取指定键名的值 @@ -60,8 +57,8 @@ func Remove(key interface{}) interface{} { } // (使用全局KV缓存对象)批量删除指定键值对 -func BatchRemove(keys []interface{}) { - cache.BatchRemove(keys) +func Removes(keys []interface{}) { + cache.Removes(keys) } // 返回缓存的所有数据键值对(不包含已过期数据) diff --git a/g/os/gcache/gcache_mem_cache.go b/g/os/gcache/gcache_mem_cache.go index 5df7e468b..33836382e 100644 --- a/g/os/gcache/gcache_mem_cache.go +++ b/g/os/gcache/gcache_mem_cache.go @@ -147,7 +147,7 @@ func (c *memCache) SetIfNotExist(key interface{}, value interface{}, expire int) } // 批量设置 -func (c *memCache) BatchSet(data map[interface{}]interface{}, expire int) { +func (c *memCache) Sets(data map[interface{}]interface{}, expire int) { expireTime := c.getInternalExpire(expire) for k, v := range data { c.dataMu.Lock() @@ -221,7 +221,7 @@ func (c *memCache) Remove(key interface{}) (value interface{}) { } // 批量删除键值对,并返回被删除的键值对数据 -func (c *memCache) BatchRemove(keys []interface{}) { +func (c *memCache) Removes(keys []interface{}) { for _, key := range keys { c.Remove(key) } diff --git a/g/os/gcfg/gcfg.go b/g/os/gcfg/gcfg.go index f72b1025c..70fcd6833 100644 --- a/g/os/gcfg/gcfg.go +++ b/g/os/gcfg/gcfg.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gcfg provides reading, caching and managing for configuration files/contents. +// Package gcfg provides reading, caching and managing for configuration. package gcfg import ( @@ -21,6 +21,8 @@ import ( "github.com/gogf/gf/g/os/gfsnotify" "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gspath" + "github.com/gogf/gf/g/os/gtime" + "time" ) const ( @@ -32,14 +34,14 @@ const ( type Config struct { name *gtype.String // Default configuration file name. paths *garray.StringArray // Searching path array. - jsons *gmap.StringInterfaceMap // The pared JSON objects for configuration files. + jsons *gmap.StrAnyMap // The pared JSON objects for configuration files. vc *gtype.Bool // Whether do violence check in value index searching. // It affects the performance when set true(false in default). } // New returns a new configuration management object. // The param specifies the default configuration file name for reading. -func New(file ...string) *Config { +func New(file...string) *Config { name := DEFAULT_CONFIG_FILE if len(file) > 0 { name = file[0] @@ -47,7 +49,7 @@ func New(file ...string) *Config { c := &Config { name : gtype.NewString(name), paths : garray.NewStringArray(), - jsons : gmap.NewStringInterfaceMap(), + jsons : gmap.NewStrAnyMap(), vc : gtype.NewBool(), } // Customized dir path from env/cmd. @@ -78,7 +80,7 @@ func (c *Config) filePath(file...string) (path string) { if len(file) > 0 { name = file[0] } - path = c.GetFilePath(name) + path = c.FilePath(name) if path == "" { buffer := bytes.NewBuffer(nil) if c.paths.Len() > 0 { @@ -101,7 +103,8 @@ func (c *Config) filePath(file...string) (path string) { } // SetPath sets the configuration directory path for file search. -// The param can be absolute or relative path, but absolute path is suggested. +// The param can be absolute or relative path, +// but absolute path is strongly recommended. func (c *Config) SetPath(path string) error { // Absolute path. realPath := gfile.RealPath(path) @@ -146,11 +149,10 @@ func (c *Config) SetPath(path string) error { c.jsons.Clear() c.paths.Clear() c.paths.Append(realPath) - //glog.Debug("[gcfg] SetPath:", realPath) return nil } -// SetViolenceCheck sets whether to perform level conflict check. +// SetViolenceCheck sets whether to perform hierarchical conflict check. // This feature needs to be enabled when there is a level symbol in the key name. // The default is off. // Turning on this feature is quite expensive, @@ -206,11 +208,17 @@ func (c *Config) AddPath(path string) error { return nil } +// Deprecated. +// Alias of FilePath. +func (c *Config) GetFilePath(file...string) (path string) { + return c.FilePath(file...) +} + // GetFilePath returns the absolute path of the specified configuration file. // If is not passed, it returns the configuration file path of the default name. // If the specified configuration file does not exist, // an empty string is returned. -func (c *Config) GetFilePath(file...string) (path string) { +func (c *Config) FilePath(file...string) (path string) { name := c.name.Val() if len(file) > 0 { name = file[0] @@ -230,7 +238,6 @@ func (c *Config) GetFilePath(file...string) (path string) { // SetFileName sets the default configuration file name. func (c *Config) SetFileName(name string) { - //glog.Debug("[gcfg] SetFileName:", name) c.name.Set(name) } @@ -282,171 +289,192 @@ func (c *Config) getJson(file...string) *gjson.Json { return nil } -func (c *Config) Get(pattern string, file...string) interface{} { - if j := c.getJson(file...); j != nil { - return j.Get(pattern) +func (c *Config) Get(pattern string, def...interface{}) interface{} { + if j := c.getJson(); j != nil { + return j.Get(pattern, def...) } return nil } -func (c *Config) GetVar(pattern string, file...string) gvar.VarRead { - if j := c.getJson(file...); j != nil { - return gvar.New(j.Get(pattern), true) +func (c *Config) GetVar(pattern string, def...interface{}) *gvar.Var { + if j := c.getJson(); j != nil { + return gvar.New(j.Get(pattern, def...), true) } return gvar.New(nil, true) } -func (c *Config) Contains(pattern string, file...string) bool { - if j := c.getJson(file...); j != nil { +func (c *Config) Contains(pattern string) bool { + if j := c.getJson(); j != nil { return j.Contains(pattern) } return false } -func (c *Config) GetMap(pattern string, file...string) map[string]interface{} { - if j := c.getJson(file...); j != nil { - return j.GetMap(pattern) +func (c *Config) GetMap(pattern string, def...interface{}) map[string]interface{} { + if j := c.getJson(); j != nil { + return j.GetMap(pattern, def...) } return nil } -func (c *Config) GetArray(pattern string, file...string) []interface{} { - if j := c.getJson(file...); j != nil { - return j.GetArray(pattern) +func (c *Config) GetArray(pattern string, def...interface{}) []interface{} { + if j := c.getJson(); j != nil { + return j.GetArray(pattern, def...) } return nil } -func (c *Config) GetString(pattern string, file...string) string { - if j := c.getJson(file...); j != nil { - return j.GetString(pattern) +func (c *Config) GetString(pattern string, def...interface{}) string { + if j := c.getJson(); j != nil { + return j.GetString(pattern, def...) } return "" } -func (c *Config) GetStrings(pattern string, file...string) []string { - if j := c.getJson(file...); j != nil { - return j.GetStrings(pattern) +func (c *Config) GetStrings(pattern string, def...interface{}) []string { + if j := c.getJson(); j != nil { + return j.GetStrings(pattern, def...) } return nil } -func (c *Config) GetInterfaces(pattern string, file...string) []interface{} { - if j := c.getJson(file...); j != nil { - return j.GetInterfaces(pattern) +func (c *Config) GetInterfaces(pattern string, def...interface{}) []interface{} { + if j := c.getJson(); j != nil { + return j.GetInterfaces(pattern, def...) } return nil } -func (c *Config) GetBool(pattern string, file...string) bool { - if j := c.getJson(file...); j != nil { - return j.GetBool(pattern) +func (c *Config) GetBool(pattern string, def...interface{}) bool { + if j := c.getJson(); j != nil { + return j.GetBool(pattern, def...) } return false } -func (c *Config) GetFloat32(pattern string, file...string) float32 { - if j := c.getJson(file...); j != nil { - return j.GetFloat32(pattern) +func (c *Config) GetFloat32(pattern string, def...interface{}) float32 { + if j := c.getJson(); j != nil { + return j.GetFloat32(pattern, def...) } return 0 } -func (c *Config) GetFloat64(pattern string, file...string) float64 { - if j := c.getJson(file...); j != nil { - return j.GetFloat64(pattern) +func (c *Config) GetFloat64(pattern string, def...interface{}) float64 { + if j := c.getJson(); j != nil { + return j.GetFloat64(pattern, def...) } return 0 } -func (c *Config) GetFloats(pattern string, file...string) []float64 { - if j := c.getJson(file...); j != nil { - return j.GetFloats(pattern) +func (c *Config) GetFloats(pattern string, def...interface{}) []float64 { + if j := c.getJson(); j != nil { + return j.GetFloats(pattern, def...) } return nil } -func (c *Config) GetInt(pattern string, file...string) int { - if j := c.getJson(file...); j != nil { - return j.GetInt(pattern) +func (c *Config) GetInt(pattern string, def...interface{}) int { + if j := c.getJson(); j != nil { + return j.GetInt(pattern, def...) } return 0 } -func (c *Config) GetInt8(pattern string, file...string) int8 { - if j := c.getJson(file...); j != nil { - return j.GetInt8(pattern) +func (c *Config) GetInt8(pattern string, def...interface{}) int8 { + if j := c.getJson(); j != nil { + return j.GetInt8(pattern, def...) } return 0 } -func (c *Config) GetInt16(pattern string, file...string) int16 { - if j := c.getJson(file...); j != nil { - return j.GetInt16(pattern) +func (c *Config) GetInt16(pattern string, def...interface{}) int16 { + if j := c.getJson(); j != nil { + return j.GetInt16(pattern, def...) } return 0 } -func (c *Config) GetInt32(pattern string, file...string) int32 { - if j := c.getJson(file...); j != nil { - return j.GetInt32(pattern) +func (c *Config) GetInt32(pattern string, def...interface{}) int32 { + if j := c.getJson(); j != nil { + return j.GetInt32(pattern, def...) } return 0 } -func (c *Config) GetInt64(pattern string, file...string) int64 { - if j := c.getJson(file...); j != nil { - return j.GetInt64(pattern) +func (c *Config) GetInt64(pattern string, def...interface{}) int64 { + if j := c.getJson(); j != nil { + return j.GetInt64(pattern, def...) } return 0 } -func (c *Config) GetInts(pattern string, file...string) []int { - if j := c.getJson(file...); j != nil { - return j.GetInts(pattern) +func (c *Config) GetInts(pattern string, def...interface{}) []int { + if j := c.getJson(); j != nil { + return j.GetInts(pattern, def...) } return nil } -func (c *Config) GetUint(pattern string, file...string) uint { - if j := c.getJson(file...); j != nil { - return j.GetUint(pattern) +func (c *Config) GetUint(pattern string, def...interface{}) uint { + if j := c.getJson(); j != nil { + return j.GetUint(pattern, def...) } return 0 } -func (c *Config) GetUint8(pattern string, file...string) uint8 { - if j := c.getJson(file...); j != nil { - return j.GetUint8(pattern) +func (c *Config) GetUint8(pattern string, def...interface{}) uint8 { + if j := c.getJson(); j != nil { + return j.GetUint8(pattern, def...) } return 0 } -func (c *Config) GetUint16(pattern string, file...string) uint16 { - if j := c.getJson(file...); j != nil { - return j.GetUint16(pattern) +func (c *Config) GetUint16(pattern string, def...interface{}) uint16 { + if j := c.getJson(); j != nil { + return j.GetUint16(pattern, def...) } return 0 } -func (c *Config) GetUint32(pattern string, file...string) uint32 { - if j := c.getJson(file...); j != nil { - return j.GetUint32(pattern) +func (c *Config) GetUint32(pattern string, def...interface{}) uint32 { + if j := c.getJson(); j != nil { + return j.GetUint32(pattern, def...) } return 0 } -func (c *Config) GetUint64(pattern string, file...string) uint64 { - if j := c.getJson(file...); j != nil { - return j.GetUint64(pattern) +func (c *Config) GetUint64(pattern string, def...interface{}) uint64 { + if j := c.getJson(); j != nil { + return j.GetUint64(pattern, def...) } return 0 } -func (c *Config) GetToStruct(pattern string, objPointer interface{}, file...string) error { - if j := c.getJson(file...); j != nil { - return j.GetToStruct(pattern, objPointer) +func (c *Config) GetTime(pattern string, format...string) time.Time { + if j := c.getJson(); j != nil { + return j.GetTime(pattern, format...) + } + return time.Time{} +} + +func (c *Config) GetDuration(pattern string, def...interface{}) time.Duration { + if j := c.getJson(); j != nil { + return j.GetDuration(pattern, def...) + } + return 0 +} + +func (c *Config) GetGTime(pattern string, format...string) *gtime.Time { + if j := c.getJson(); j != nil { + return j.GetGTime(pattern, format...) + } + return nil +} + +func (c *Config) GetToStruct(pattern string, pointer interface{}, def...interface{}) error { + if j := c.getJson(); j != nil { + return j.GetToStruct(pattern, pointer) } return errors.New("config file not found") } diff --git a/g/os/gcfg/gcfg_config.go b/g/os/gcfg/gcfg_config.go index 2145a6f9a..abbfdd48a 100644 --- a/g/os/gcfg/gcfg_config.go +++ b/g/os/gcfg/gcfg_config.go @@ -10,7 +10,7 @@ import "github.com/gogf/gf/g/container/gmap" var ( // Customized configuration content. - configs = gmap.NewStringStringMap() + configs = gmap.NewStrStrMap() ) // SetContent sets customized configuration content for specified . diff --git a/g/os/gcfg/gcfg_instance.go b/g/os/gcfg/gcfg_instance.go index fce9e0f28..c93f22966 100644 --- a/g/os/gcfg/gcfg_instance.go +++ b/g/os/gcfg/gcfg_instance.go @@ -16,12 +16,12 @@ const ( ) var ( // Instances map. - instances = gmap.NewStringInterfaceMap() + instances = gmap.NewStrAnyMap() ) // Instance returns an instance of Config with default settings. // The param is the name for the instance. -func Instance(name ...string) *Config { +func Instance(name...string) *Config { key := DEFAULT_GROUP_NAME if len(name) > 0 { key = name[0] diff --git a/g/os/gcfg/gcfg_z_unit_test.go b/g/os/gcfg/gcfg_z_unit_test.go index ae6f9f7e3..91d3da165 100644 --- a/g/os/gcfg/gcfg_z_unit_test.go +++ b/g/os/gcfg/gcfg_z_unit_test.go @@ -71,7 +71,7 @@ array = [1,2,3] "disk" : "127.0.0.1:6379,0", "cache" : "127.0.0.1:6379,1", }) - gtest.AssertEQ(c.GetFilePath(), gfile.Pwd() + gfile.Separator + path) + gtest.AssertEQ(c.FilePath(), gfile.Pwd() + gfile.Separator + path) }) } @@ -197,7 +197,7 @@ func Test_SetFileName(t *testing.T) { "disk" : "127.0.0.1:6379,0", "cache" : "127.0.0.1:6379,1", }) - gtest.AssertEQ(c.GetFilePath(), gfile.Pwd() + gfile.Separator + path) + gtest.AssertEQ(c.FilePath(), gfile.Pwd() + gfile.Separator + path) }) } @@ -265,7 +265,7 @@ func Test_Instance(t *testing.T) { "disk" : "127.0.0.1:6379,0", "cache" : "127.0.0.1:6379,1", }) - gtest.AssertEQ(c.GetFilePath(), gfile.Pwd() + gfile.Separator + path) + gtest.AssertEQ(c.FilePath(), gfile.Pwd() + gfile.Separator + path) }) } \ No newline at end of file diff --git a/g/os/gcmd/gcmd_option.go b/g/os/gcmd/gcmd_option.go index 4cc0147f4..ca7b2caa1 100644 --- a/g/os/gcmd/gcmd_option.go +++ b/g/os/gcmd/gcmd_option.go @@ -25,8 +25,8 @@ func (c *gCmdOption) Get(key string, def...string) string { return "" } -// GetVar returns the option value as gvar.VarRead object specified by , +// GetVar returns the option value as *gvar.Var object specified by , // if value does not exist, then returns as its default value. -func (c *gCmdOption) GetVar(key string, def...string) gvar.VarRead { - return gvar.NewRead(c.Get(key, def...), true) +func (c *gCmdOption) GetVar(key string, def...string) *gvar.Var { + return gvar.New(c.Get(key, def...), true) } diff --git a/g/os/gcmd/gcmd_value.go b/g/os/gcmd/gcmd_value.go index b1144b6ac..9f9b006b9 100644 --- a/g/os/gcmd/gcmd_value.go +++ b/g/os/gcmd/gcmd_value.go @@ -25,8 +25,8 @@ func (c *gCmdValue) Get(index int, def...string) string { return "" } -// GetVar returns value by index as gvar.VarRead object, +// GetVar returns value by index as *gvar.Var object, // if value does not exist, then returns as its default value. -func (c *gCmdValue) GetVar(index int, def...string) gvar.VarRead { - return gvar.NewRead(c.Get(index, def...), true) +func (c *gCmdValue) GetVar(index int, def...string) *gvar.Var { + return gvar.New(c.Get(index, def...), true) } diff --git a/g/os/gcron/gcron_cron.go b/g/os/gcron/gcron_cron.go index b1a21aa88..2a9bf402a 100644 --- a/g/os/gcron/gcron_cron.go +++ b/g/os/gcron/gcron_cron.go @@ -18,11 +18,11 @@ import ( ) type Cron struct { - idGen *gtype.Int64 // Used for unique name generation. - status *gtype.Int // Timed task status(0: Not Start; 1: Running; 2: Stopped; -1: Closed) - entries *gmap.StringInterfaceMap // All timed task entries. - logPath *gtype.String // Logging path(folder). - logLevel *gtype.Int // Logging level. + idGen *gtype.Int64 // Used for unique name generation. + status *gtype.Int // Timed task status(0: Not Start; 1: Running; 2: Stopped; -1: Closed) + entries *gmap.StrAnyMap // All timed task entries. + logPath *gtype.String // Logging path(folder). + logLevel *gtype.Int // Logging level. } // New returns a new Cron object with default settings. @@ -30,7 +30,7 @@ func New() *Cron { return &Cron { idGen : gtype.NewInt64(), status : gtype.NewInt(STATUS_RUNNING), - entries : gmap.NewStringInterfaceMap(), + entries : gmap.NewStrAnyMap(), logPath : gtype.NewString(), logLevel : gtype.NewInt(glog.LEVEL_PROD), } diff --git a/g/os/gfpool/gfpool.go b/g/os/gfpool/gfpool.go index 00730c0e1..034c43e42 100644 --- a/g/os/gfpool/gfpool.go +++ b/g/os/gfpool/gfpool.go @@ -39,7 +39,7 @@ type File struct { } // 全局指针池,expire < 0表示不过期,expire = 0表示使用完立即回收,expire > 0表示超时回收 -var pools = gmap.NewStringInterfaceMap() +var pools = gmap.NewStrAnyMap() // 获得文件对象,并自动创建指针池(过期时间单位:毫秒) func Open(path string, flag int, perm os.FileMode, expire...int) (file *File, err error) { diff --git a/g/os/gfsnotify/gfsnotify.go b/g/os/gfsnotify/gfsnotify.go index d3199a344..72724e9c7 100644 --- a/g/os/gfsnotify/gfsnotify.go +++ b/g/os/gfsnotify/gfsnotify.go @@ -25,7 +25,7 @@ type Watcher struct { watcher *fsnotify.Watcher // 底层fsnotify对象 events *gqueue.Queue // 过滤后的事件通知,不会出现重复事件 cache *gcache.Cache // 缓存对象,主要用于事件重复过滤 - callbacks *gmap.StringInterfaceMap // 注册的所有绝对路径(文件/目录)及其对应的回调函数列表map + callbacks *gmap.StrAnyMap // 注册的所有绝对路径(文件/目录)及其对应的回调函数列表map closeChan chan struct{} // 关闭事件 } @@ -69,7 +69,7 @@ var ( // 默认的watchers是否初始化,使用时才创建 watcherInited = gtype.NewBool() // 回调方法ID与对象指针的映射哈希表,用于根据ID快速查找回调对象 - callbackIdMap = gmap.NewIntInterfaceMap() + callbackIdMap = gmap.NewIntAnyMap() // 回调函数的ID生成器(原子操作) callbackIdGenerator = gtype.NewInt() ) @@ -80,7 +80,7 @@ func New() (*Watcher, error) { cache : gcache.New(), events : gqueue.New(), closeChan : make(chan struct{}), - callbacks : gmap.NewStringInterfaceMap(), + callbacks : gmap.NewStrAnyMap(), } if watcher, err := fsnotify.NewWatcher(); err == nil { w.watcher = watcher diff --git a/g/os/gmlock/gmlock_locker.go b/g/os/gmlock/gmlock_locker.go index ae2b3acb8..a43fb55e8 100644 --- a/g/os/gmlock/gmlock_locker.go +++ b/g/os/gmlock/gmlock_locker.go @@ -14,13 +14,13 @@ import ( // 内存锁管理对象 type Locker struct { - m *gmap.StringInterfaceMap + m *gmap.StrAnyMap } // 创建一把内存锁, 底层使用的是Mutex func New() *Locker { return &Locker{ - m : gmap.NewStringInterfaceMap(), + m : gmap.NewStrAnyMap(), } } diff --git a/g/os/gproc/gproc_comm.go b/g/os/gproc/gproc_comm.go index 253b59883..441bcb0c0 100644 --- a/g/os/gproc/gproc_comm.go +++ b/g/os/gproc/gproc_comm.go @@ -16,10 +16,10 @@ import ( ) // 本地进程通信接收消息队列(按照分组进行构建的map,键值为*gqueue.Queue对象) -var commReceiveQueues = gmap.NewStringInterfaceMap() +var commReceiveQueues = gmap.NewStrAnyMap() // (用于发送)已建立的PID对应的Conn通信对象,键值为一个Pool,防止并行使用同一个通信对象造成数据重叠 -var commPidConnMap = gmap.NewIntInterfaceMap() +var commPidConnMap = gmap.NewIntAnyMap() // TCP通信数据结构定义 type Msg struct { diff --git a/g/os/gproc/gproc_manager.go b/g/os/gproc/gproc_manager.go index 42d4ad3fb..4e05858e0 100644 --- a/g/os/gproc/gproc_manager.go +++ b/g/os/gproc/gproc_manager.go @@ -14,13 +14,13 @@ import ( // 进程管理器 type Manager struct { - processes *gmap.IntInterfaceMap // 所管理的子进程map + processes *gmap.IntAnyMap // 所管理的子进程map } // 创建一个进程管理器 func NewManager() *Manager { return &Manager{ - processes : gmap.NewIntInterfaceMap(), + processes : gmap.NewIntAnyMap(), } } diff --git a/g/os/gspath/gspath.go b/g/os/gspath/gspath.go index 7cb98f7b5..2792f1bd6 100644 --- a/g/os/gspath/gspath.go +++ b/g/os/gspath/gspath.go @@ -25,8 +25,8 @@ import ( // 文件目录搜索管理对象 type SPath struct { - paths *garray.StringArray // 搜索路径,按照优先级进行排序 - cache *gmap.StringStringMap // 搜索结果缓存map(如果未nil表示未启用缓存功能) + paths *garray.StringArray // 搜索路径,按照优先级进行排序 + cache *gmap.StrStrMap // 搜索结果缓存map(如果未nil表示未启用缓存功能) } // 文件搜索缓存项 @@ -37,8 +37,8 @@ type SPathCacheItem struct { var ( // 单个目录路径对应的SPath对象指针,用于路径检索对象复用 - pathsMap = gmap.NewStringInterfaceMap() - pathsCacheMap = gmap.NewStringInterfaceMap() + pathsMap = gmap.NewStrAnyMap() + pathsCacheMap = gmap.NewStrAnyMap() ) // 创建一个搜索对象 @@ -47,7 +47,7 @@ func New(path string, cache bool) *SPath { paths : garray.NewStringArray(), } if cache { - sp.cache = gmap.NewStringStringMap() + sp.cache = gmap.NewStrStrMap() } if len(path) > 0 { if _, err := sp.Add(path); err != nil { diff --git a/g/os/gtimer/gtimer.go b/g/os/gtimer/gtimer.go index 9454a4c48..6ef8e97ee 100644 --- a/g/os/gtimer/gtimer.go +++ b/g/os/gtimer/gtimer.go @@ -34,7 +34,7 @@ var ( // 默认定时器属性参数值 defaultSlots = cmdenv.Get("gf.gtimer.slots", gDEFAULT_SLOT_NUMBER).Int() defaultLevel = cmdenv.Get("gf.gtimer.level", gDEFAULT_WHEEL_LEVEL).Int() - defaultInterval = cmdenv.Get("gf.gtimer.interval", gDEFAULT_WHEEL_INTERVAL).TimeDuration()*time.Millisecond + defaultInterval = cmdenv.Get("gf.gtimer.interval", gDEFAULT_WHEEL_INTERVAL).Duration()*time.Millisecond // 默认的wheel管理对象 defaultTimer = New(defaultSlots, defaultInterval, defaultLevel) ) diff --git a/g/os/gview/gview_doparse.go b/g/os/gview/gview_doparse.go index c1fc12d9e..b122d977c 100644 --- a/g/os/gview/gview_doparse.go +++ b/g/os/gview/gview_doparse.go @@ -22,7 +22,7 @@ import ( var ( // Templates cache map for template folder. - templates = gmap.NewStringInterfaceMap() + templates = gmap.NewStrAnyMap() ) // getTemplate returns the template object associated with given template folder . diff --git a/g/os/gview/gview_instance.go b/g/os/gview/gview_instance.go index 703c093fc..b4019e5dd 100644 --- a/g/os/gview/gview_instance.go +++ b/g/os/gview/gview_instance.go @@ -14,7 +14,7 @@ const ( ) var ( // Instances map. - instances = gmap.NewStringInterfaceMap() + instances = gmap.NewStrAnyMap() ) // Instance returns an instance of View with default settings. diff --git a/g/test/gtest/gtest.go b/g/test/gtest/gtest.go index 80f58bd05..a2d155c58 100644 --- a/g/test/gtest/gtest.go +++ b/g/test/gtest/gtest.go @@ -4,7 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gtest provides convenient test utils for unit testing. +// Package gtest provides convenient test utilities for unit testing. package gtest import ( @@ -111,8 +111,8 @@ func AssertGT(value, expect interface{}) { } } -// See AssertGE. // Deprecated. +// See AssertGE. func AssertGTE(value, expect interface{}) { AssertGE(value, expect) } @@ -163,8 +163,8 @@ func AssertLT(value, expect interface{}) { } } -// See AssertLE. // Deprecated. +// See AssertLE. func AssertLTE(value, expect interface{}) { AssertLE(value, expect) } @@ -287,7 +287,7 @@ func compareMap(value, expect interface{}) error { } } } else { - return fmt.Errorf(`[ASSERT] EXPECT MAP LENGTH %d == %d`, rvExpect.Len(), rvValue.Len()) + return fmt.Errorf(`[ASSERT] EXPECT MAP LENGTH %d == %d`, rvValue.Len(), rvExpect.Len()) } } else { return fmt.Errorf(`[ASSERT] EXPECT VALUE TO BE A MAP`) diff --git a/g/util/gconv/gconv.go b/g/util/gconv/gconv.go index f3a95c205..af64db5c3 100644 --- a/g/util/gconv/gconv.go +++ b/g/util/gconv/gconv.go @@ -5,9 +5,6 @@ // You can obtain one at https://github.com/gogf/gf. // Package gconv implements powerful and easy-to-use converting functionality for any types of variables. -// -// 类型转换, -// 内部使用了bytes作为底层转换类型,效率很高。 package gconv import ( @@ -18,13 +15,13 @@ import ( "strings" ) -// 转换为string类型的接口 +// Type assert api for String(). type apiString interface { String() string } var ( - // 为空的字符串 + // Empty strings. emptyStringMap = map[string]struct{}{ "" : struct {}{}, "0" : struct {}{}, @@ -33,8 +30,10 @@ var ( } ) -// 将变量i转换为字符串指定的类型t,非必须参数extraParams用以额外的参数传递 -func Convert(i interface{}, t string, extraParams...interface{}) interface{} { + +// Convert converts the variable to the type , the type is specified by string. +// The unnecessary parameter is used for additional parameter passing. +func Convert(i interface{}, t string, params...interface{}) interface{} { switch t { case "int": return Int(i) case "int8": return Int8(i) @@ -53,28 +52,41 @@ func Convert(i interface{}, t string, extraParams...interface{}) interface{} { case "[]byte": return Bytes(i) case "[]int": return Ints(i) case "[]string": return Strings(i) - case "time.Time": - if len(extraParams) > 0 { - return Time(i, String(extraParams[0])) + + case "Time", "time.Time": + if len(params) > 0 { + return Time(i, String(params[0])) } return Time(i) + case "gtime.Time": - if len(extraParams) > 0 { - return GTime(i, String(extraParams[0])) + if len(params) > 0 { + return GTime(i, String(params[0])) } return *GTime(i) - case "*gtime.Time": - if len(extraParams) > 0 { - return GTime(i, String(extraParams[0])) + + case "GTime", "*gtime.Time": + if len(params) > 0 { + return GTime(i, String(params[0])) } return GTime(i) - case "time.Duration": return TimeDuration(i) + + case "Duration", "time.Duration": + return Duration(i) default: return i } } -// 转换为二进制[]byte +// Byte converts to byte. +func Byte(i interface{}) byte { + if v, ok := i.(byte); ok { + return v + } + return byte(Uint8(i)) +} + +// Bytes converts to []byte. func Bytes(i interface{}) []byte { if i == nil { return nil @@ -87,7 +99,24 @@ func Bytes(i interface{}) []byte { } } -// 基础的字符串类型转换 +// Rune converts to rune. +func Rune(i interface{}) rune { + if v, ok := i.(rune); ok { + return v + } + return rune(Int32(i)) +} + +// Runes converts to []rune. +func Runes(i interface{}) []rune { + if v, ok := i.([]rune); ok { + return v + } + return []rune(String(i)) +} + + +// String converts to string. func String(i interface{}) string { if i == nil { return "" @@ -110,17 +139,19 @@ func String(i interface{}) string { case []byte: return string(value) default: if f, ok := value.(apiString); ok { - // 如果变量实现了String()接口,那么使用该接口执行转换 + // If the variable implements the String() interface, + // then use that interface to perform the conversion return f.String() } else { - // 默认使用json进行字符串转换 + // Finally we use json.Marshal to convert. jsonContent, _ := json.Marshal(value) return string(jsonContent) } } } -//false: false, "", 0, "false", "off", empty slice/map +// Bool converts to bool. +// It returns false if is: false, "", 0, "false", "off", empty slice/map. func Bool(i interface{}) bool { if i == nil { return false @@ -151,6 +182,7 @@ func Bool(i interface{}) bool { } } +// Int converts to int. func Int(i interface{}) int { if i == nil { return 0 @@ -161,6 +193,7 @@ func Int(i interface{}) int { return int(Int64(i)) } +// Int8 converts to int8. func Int8(i interface{}) int8 { if i == nil { return 0 @@ -171,6 +204,7 @@ func Int8(i interface{}) int8 { return int8(Int64(i)) } +// Int16 converts to int16. func Int16(i interface{}) int16 { if i == nil { return 0 @@ -181,6 +215,7 @@ func Int16(i interface{}) int16 { return int16(Int64(i)) } +// Int32 converts to int32. func Int32(i interface{}) int32 { if i == nil { return 0 @@ -191,6 +226,7 @@ func Int32(i interface{}) int32 { return int32(Int64(i)) } +// Int64 converts to int64. func Int64(i interface{}) int64 { if i == nil { return 0 @@ -218,27 +254,28 @@ func Int64(i interface{}) int64 { return 0 default: s := String(value) - // 按照十六进制解析 + // Hexadecimal if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { if v, e := strconv.ParseInt(s[2 : ], 16, 64); e == nil { return v } } - // 按照八进制解析 + // Octal if len(s) > 1 && s[0] == '0' { if v, e := strconv.ParseInt(s[1 : ], 8, 64); e == nil { return v } } - // 按照十进制解析 + // Decimal if v, e := strconv.ParseInt(s, 10, 64); e == nil { return v } - // 按照浮点数解析 + // Float64 return int64(Float64(value)) } } +// Uint converts to uint. func Uint(i interface{}) uint { if i == nil { return 0 @@ -249,6 +286,7 @@ func Uint(i interface{}) uint { return uint(Uint64(i)) } +// Uint8 converts to uint8. func Uint8(i interface{}) uint8 { if i == nil { return 0 @@ -259,6 +297,7 @@ func Uint8(i interface{}) uint8 { return uint8(Uint64(i)) } +// Uint16 converts to uint16. func Uint16(i interface{}) uint16 { if i == nil { return 0 @@ -269,6 +308,7 @@ func Uint16(i interface{}) uint16 { return uint16(Uint64(i)) } +// Uint32 converts to uint32. func Uint32(i interface{}) uint32 { if i == nil { return 0 @@ -279,6 +319,7 @@ func Uint32(i interface{}) uint32 { return uint32(Uint64(i)) } +// Uint64 converts to uint64. func Uint64(i interface{}) uint64 { if i == nil { return 0 @@ -303,27 +344,28 @@ func Uint64(i interface{}) uint64 { return 0 default: s := String(value) - // 按照十六进制解析 + // Hexadecimal if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { if v, e := strconv.ParseUint(s[2 : ], 16, 64); e == nil { return v } } - // 按照八进制解析 + // Octal if len(s) > 1 && s[0] == '0' { if v, e := strconv.ParseUint(s[1 : ], 8, 64); e == nil { return v } } - // 按照十进制解析 + // Decimal if v, e := strconv.ParseUint(s, 10, 64); e == nil { return v } - // 按照浮点数解析 + // Float64 return uint64(Float64(value)) } } +// Float32 converts to float32. func Float32 (i interface{}) float32 { if i == nil { return 0 @@ -335,6 +377,7 @@ func Float32 (i interface{}) float32 { return float32(v) } +// Float64 converts to float64. func Float64 (i interface{}) float64 { if i == nil { return 0 diff --git a/g/util/gconv/gconv_map.go b/g/util/gconv/gconv_map.go index 7859fe330..94f6f72e9 100644 --- a/g/util/gconv/gconv_map.go +++ b/g/util/gconv/gconv_map.go @@ -13,17 +13,18 @@ import ( "strings" ) -// 任意类型转换为 map[string]interface{} 类型, -// 如果给定的输入参数i不是map类型,那么转换会失败,返回nil. -// 当i为struct对象时,第二个参数noTagCheck表示不检测json标签,否则将会使用json tag作为map的键名。 -func Map(value interface{}, noTagCheck...bool) map[string]interface{} { +// Map converts any variable to map[string]interface{}. +// If the parameter is not a map type, then the conversion will fail and returns nil. +// If is a struct object, the second parameter specifies the most priority +// tags that will be detected, otherwise it detects the tags in order of: gconv, json. +func Map(value interface{}, tags...string) map[string]interface{} { if value == nil { return nil } if r, ok := value.(map[string]interface{}); ok { return r } else { - // 仅对常见的几种map组合进行断言,最后才会使用反射 + // Only assert the common combination type of maps, and finally use reflection. m := make(map[string]interface{}) switch value.(type) { case map[interface{}]interface{}: @@ -50,7 +51,6 @@ func Map(value interface{}, noTagCheck...bool) map[string]interface{} { for k, v := range value.(map[interface{}]float64) { m[String(k)] = v } - case map[string]bool: for k, v := range value.(map[string]bool) { m[k] = v @@ -84,11 +84,11 @@ func Map(value interface{}, noTagCheck...bool) map[string]interface{} { for k, v := range value.(map[uint]string) { m[String(k)] = v } - // 不是常见类型,则使用反射 + // Not a common type, use reflection default: rv := reflect.ValueOf(value) kind := rv.Kind() - // 如果是指针,那么需要转换到指针对应的数据项,以便识别真实的类型 + // If it is a pointer, we should find its real data type. if kind == reflect.Ptr { rv = rv.Elem() kind = rv.Kind() @@ -100,26 +100,38 @@ func Map(value interface{}, noTagCheck...bool) map[string]interface{} { m[String(k.Interface())] = rv.MapIndex(k).Interface() } case reflect.Struct: - rt := rv.Type() - name := "" + rt := rv.Type() + name := "" + gconvTag := "gconv" + tagArray := []string{gconvTag, "json"} + switch len(tags) { + case 0: + // No need handle. + case 1: + tagArray = strings.Split(tags[0], ",") + default: + tagArray = tags + } + if gstr.SearchArray(tagArray, gconvTag) < 0 { + tagArray = append(tagArray, gconvTag) + } for i := 0; i < rv.NumField(); i++ { - // 只转换公开属性 + // Only convert the public attributes. fieldName := rt.Field(i).Name if !gstr.IsLetterUpper(fieldName[0]) { continue } - name = "" - // 检查tag, 支持gconv, json标签, 优先使用gconv - if len(noTagCheck) == 0 || !noTagCheck[0] { - tag := rt.Field(i).Tag - if name = tag.Get("gconv"); name == "" { - name = tag.Get("json") + name = "" + fieldTag := rt.Field(i).Tag + for _, tag := range tagArray { + if name = fieldTag.Get(tag); name != "" { + break } } if name == "" { name = strings.TrimSpace(fieldName) } else { - // 支持标准库json特性: -, omitempty + // Support json tag feature: -, omitempty name = strings.TrimSpace(name) if name == "-" { continue diff --git a/g/util/gconv/gconv_slice.go b/g/util/gconv/gconv_slice.go index e8eb98e70..7cae7c003 100644 --- a/g/util/gconv/gconv_slice.go +++ b/g/util/gconv/gconv_slice.go @@ -11,7 +11,7 @@ import ( "reflect" ) -// 任意类型转换为[]int类型 +// Ints converts to []int. func Ints(i interface{}) []int { if i == nil { return nil @@ -84,7 +84,7 @@ func Ints(i interface{}) []int { } } -// 任意类型转换为[]string类型 +// Strings converts to []string. func Strings(i interface{}) []string { if i == nil { return nil @@ -157,7 +157,7 @@ func Strings(i interface{}) []string { } } -// 将类型转换为[]float64类型 +// Strings converts to []float64. func Floats(i interface{}) []float64 { if i == nil { return nil @@ -230,7 +230,7 @@ func Floats(i interface{}) []float64 { } } -// 任意类型转换为[]interface{}类型 +// Interfaces converts to []interface{}. func Interfaces(i interface{}) []interface{} { if i == nil { return nil @@ -296,11 +296,11 @@ func Interfaces(i interface{}) []interface{} { for _, v := range i.([]float64) { array = append(array, v) } - // 不是常见类型,则使用反射 default: + // Finally we use reflection. rv := reflect.ValueOf(i) kind := rv.Kind() - // 如果是指针,那么需要转换到指针对应的数据项,以便识别真实的类型 + // If it's pointer, find the real type. if kind == reflect.Ptr { rv = rv.Elem() kind = rv.Kind() @@ -314,7 +314,7 @@ func Interfaces(i interface{}) []interface{} { case reflect.Struct: rt := rv.Type() for i := 0; i < rv.NumField(); i++ { - // 只获取公开属性 + // Only public attributes. if !gstr.IsLetterUpper(rt.Field(i).Name[0]) { continue } @@ -328,7 +328,7 @@ func Interfaces(i interface{}) []interface{} { } } -// 将类型转换为[]map[string]interface{}类型. +// Maps converts to []map[string]interface{}. func Maps(i interface{}) []map[string]interface{} { if i == nil { return nil diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go index 87e220006..879bf7826 100644 --- a/g/util/gconv/gconv_struct.go +++ b/g/util/gconv/gconv_struct.go @@ -15,12 +15,18 @@ import ( "strings" ) -// 将params键值对参数映射到对应的struct对象属性上, -// 第三个参数mapping为非必需,表示自定义名称与属性名称的映射关系。 -// 需要注意: -// 1、第二个参数应当为struct对象指针; -// 2、struct对象的**公开属性(首字母大写)**才能被映射赋值; -// 3、map中的键名可以为小写,映射转换时会自动将键名首字母转为大写做匹配映射,如果无法匹配则忽略; +// Struct maps the params key-value pairs to the corresponding struct object properties. +// The third parameter mapping is unnecessary, indicating the mapping between the custom name +// and the attribute name. +// +// Note: +// 1. The can be any type of may/struct, usually a map; +// 2. The second parameter should be a pointer to the struct object; +// 3. Only the public attributes of struct object can be mapped; +// 4. If is a map, the key of the map can be lowercase. +// It will automatically convert the first letter of the key to uppercase +// in mapping procedure to do the matching. +// If it does not match, ignore the key; func Struct(params interface{}, objPointer interface{}, attrMapping...map[string]string) error { if params == nil { return errors.New("params cannot be nil") diff --git a/g/util/gconv/gconv_time.go b/g/util/gconv/gconv_time.go index 3a464b2ab..d6e5ea337 100644 --- a/g/util/gconv/gconv_time.go +++ b/g/util/gconv/gconv_time.go @@ -7,28 +7,38 @@ package gconv import ( - "time" - "github.com/gogf/gf/g/os/gtime" - "github.com/gogf/gf/g/text/gstr" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/text/gstr" + "time" ) -// 将变量i转换为time.Time类型 +// Time converts to time.Time. func Time(i interface{}, format...string) time.Time { return GTime(i, format...).Time } -// 将变量i转换为time.Time类型 -func TimeDuration(i interface{}) time.Duration { - return time.Duration(Int64(i)) +// Duration converts to time.Duration. +// If is string, then it uses time.ParseDuration to convert it. +// If is numeric, then it converts as nanoseconds. +func Duration(i interface{}) time.Duration { + s := String(i) + if !gstr.IsNumeric(s) { + d, _ := time.ParseDuration(s) + return d + } + return time.Duration(Int64(i)) } -// 将变量i转换为time.Time类型, 自动识别i为时间戳或者标准化的时间字符串。 +// GTime converts to *gtime.Time. +// The parameter can be used to specify the format of . +// If no given, it converts using gtime.NewFromTimeStamp if is numeric, +// or using gtime.StrToTime if is string. func GTime(i interface{}, format...string) *gtime.Time { s := String(i) if len(s) == 0 { return gtime.New() } - // 优先使用用户输入日期格式进行转换 + // Priority conversion using given format. if len(format) > 0 { t, _ := gtime.StrToTimeFormat(s, format[0]) return t diff --git a/g/util/gconv/gconv_z_bench_test.go b/g/util/gconv/gconv_z_bench_test.go index 20259372c..8a9b98c89 100644 --- a/g/util/gconv/gconv_z_bench_test.go +++ b/g/util/gconv/gconv_z_bench_test.go @@ -101,7 +101,7 @@ func BenchmarkTime(b *testing.B) { func BenchmarkTimeDuration(b *testing.B) { for i := 0; i < b.N; i++ { - TimeDuration(value) + Duration(value) } } diff --git a/g/util/gconv/gconv_z_unit_time_test.go b/g/util/gconv/gconv_z_unit_time_test.go index 69351fda6..7195f89f6 100644 --- a/g/util/gconv/gconv_z_unit_time_test.go +++ b/g/util/gconv/gconv_z_unit_time_test.go @@ -20,6 +20,6 @@ func Test_Time(t *testing.T) { t1 := "2011-10-10 01:02:03.456" gtest.AssertEQ(gconv.GTime(t1), gtime.NewFromStr(t1)) gtest.AssertEQ(gconv.Time(t1), gtime.NewFromStr(t1).Time) - gtest.AssertEQ(gconv.TimeDuration(100), 100*time.Nanosecond) + gtest.AssertEQ(gconv.Duration(100), 100*time.Nanosecond) }) } diff --git a/g/util/grand/grand.go b/g/util/grand/grand.go index 77278110f..22ecafb0c 100644 --- a/g/util/grand/grand.go +++ b/g/util/grand/grand.go @@ -4,9 +4,7 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package grand provides high performance API for random functionality. -// -// 随机数管理. +// Package grand provides high performance random string generation functionality. package grand var ( @@ -14,86 +12,96 @@ var ( digits = []rune("0123456789") ) -// 随机计算是否满足给定的概率(分子/分母) +// Meet randomly calculate whether the given probability / is met. func Meet(num, total int) bool { return Intn(total) < num } -// 随机计算是否满足给定的概率(float32) +// MeetProb randomly calculate whether the given probability is met. func MeetProb(prob float32) bool { return Intn(1e7) < int(prob*1e7) } -// Rand 别名, 返回: [min, max] +// N returns a random int between min and max - [min, max]. func N (min, max int) int { - return Rand(min, max) + if min >= max { + return min + } + if min >= 0 { + // Because Intn dose not support negative number, + // so we should first shift the value to left, + // then call Intn to produce the random number, + // and finally shift the result to right. + return Intn(max - (min - 0) + 1) + (min - 0) + } + if min < 0 { + // Because Intn dose not support negative number, + // so we should first shift the value to right, + // then call Intn to produce the random number, + // and finally shift the result to left. + return Intn(max + (0 - min) + 1) - (0 - min) + } + return 0 } -// 获得一个 min, max 之间的随机数: [min, max] +// Deprecated. +// Alias of N. func Rand (min, max int) int { - if min >= max { - return min - } - if min >= 0 { - // 数值往左平移,再使用底层随机方法获得随机数,随后将结果数值往右平移 - return Intn(max - (min - 0) + 1) + (min - 0) - } - if min < 0 { - // 数值往右平移,再使用底层随机方法获得随机数,随后将结果数值往左平移 - return Intn(max + (0 - min) + 1) - (0 - min) - } - return 0 + return N(min, max) } -// RandStr 别名 +// Str returns a random string which contains digits and letters, and its length is . func Str(n int) string { - return RandStr(n) + b := make([]rune, n) + for i := range b { + if Intn(2) == 1 { + b[i] = digits[Intn(10)] + } else { + b[i] = letters[Intn(52)] + } + } + return string(b) } -// 获得指定长度的随机字符串(可能包含数字和字母) +// Deprecated. +// Alias of Str. func RandStr(n int) string { - b := make([]rune, n) - for i := range b { - if Intn(2) == 1 { - b[i] = digits[Intn(10)] - } else { - b[i] = letters[Intn(52)] - } - } - return string(b) + return Str(n) } -// RandDigits 别名 +// Digits returns a random string which contains only digits, and its length is . func Digits(n int) string { - return RandDigits(n) + b := make([]rune, n) + for i := range b { + b[i] = digits[Intn(10)] + } + return string(b) + } -// 获得指定长度的随机数字字符串 +// Deprecated. +// Alias of Digits. func RandDigits(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = digits[Intn(10)] - } - return string(b) + return Digits(n) } -// RandLetters 别名 +// Letters returns a random string which contains only letters, and its length is . func Letters(n int) string { - return RandLetters(n) + b := make([]rune, n) + for i := range b { + b[i] = letters[Intn(52)] + } + return string(b) + } -// 获得指定长度的随机字母字符串 +// Deprecated. +// Alias of Letters. func RandLetters(n int) string { - b := make([]rune, n) - for i := range b { - b[i] = letters[Intn(52)] - } - return string(b) + return Letters(n) } // Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n). -// -// 返回[0, n)的随机数组成的slice。 func Perm(n int) []int { m := make([]int, n) for i := 0; i < n; i++ { diff --git a/g/util/grand/grand_intn.go b/g/util/grand/grand_intn.go index 14dcc032e..cb4cf3d1e 100644 --- a/g/util/grand/grand_intn.go +++ b/g/util/grand/grand_intn.go @@ -13,14 +13,18 @@ import ( ) const ( - gBUFFER_SIZE = 10000 // 缓冲区uint32数量大小 + // Buffer size for uint32 random number. + gBUFFER_SIZE = 10000 ) var ( + // Buffer chan. bufferChan = make(chan uint32, gBUFFER_SIZE) ) -// 使用缓冲区实现快速的随机数生成 +// It uses a asychronous goroutine to produce the random number, +// and a buffer chan to store the random number. So it has high performance +// to generate random number. func init() { step := 0 buffer := make([]byte, 1024) @@ -54,7 +58,11 @@ func init() { }() } -// 自定义的 rand.Intn ,绝对随机, 返回: [0, max) +// Intn returns a int number which is between 0 and max - [0, max). +// +// Note: +// 1. The result is greater than or equal to 0, but less than ; +// 2. The result number is 32bit and less than math.MaxUint32. func Intn (max int) int { n := int(<- bufferChan)%max if (max > 0 && n < 0) || (max < 0 && n > 0) { diff --git a/g/util/gutil/gutil.go b/g/util/gutil/gutil.go index ba1eff710..b664df319 100644 --- a/g/util/gutil/gutil.go +++ b/g/util/gutil/gutil.go @@ -4,23 +4,20 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gutil provides some uncategorized util functions. -// -// 工具包. +// Package gutil provides utility functions. package gutil import ( - "bytes" - "encoding/json" - "fmt" - "github.com/gogf/gf/g/internal/empty" - "github.com/gogf/gf/g/util/gconv" - "os" - "reflect" - "runtime" + "bytes" + "encoding/json" + "fmt" + "github.com/gogf/gf/g/internal/empty" + "github.com/gogf/gf/g/util/gconv" + "os" + "runtime" ) -// 格式化打印变量 +// Dump prints variables to stdout with more manually readable. func Dump(i...interface{}) { s := Export(i...) if s != "" { @@ -28,25 +25,16 @@ func Dump(i...interface{}) { } } -// 格式化导出变量 +// Export returns variables as a string with more manually readable. func Export(i...interface{}) string { buffer := bytes.NewBuffer(nil) for _, v := range i { if b, ok := v.([]byte); ok { buffer.Write(b) } else { - // 主要针对 map[interface{}]* 进行处理,json无法进行encode, - // 这里强制对所有map进行反射处理转换 - refValue := reflect.ValueOf(v) - if refValue.Kind() == reflect.Map { - m := make(map[string]interface{}) - keys := refValue.MapKeys() - for _, k := range keys { - m[gconv.String(k.Interface())] = refValue.MapIndex(k).Interface() - } - v = m + if m := gconv.Map(v); m != nil { + v = m } - // JSON格式化 encoder := json.NewEncoder(buffer) encoder.SetEscapeHTML(false) encoder.SetIndent("", "\t") @@ -58,7 +46,7 @@ func Export(i...interface{}) string { return buffer.String() } -// 打印完整的调用回溯信息 +// PrintBacktrace prints the caller backtrace to stdout. func PrintBacktrace() { index := 1 buffer := bytes.NewBuffer(nil) @@ -73,12 +61,12 @@ func PrintBacktrace() { fmt.Print(buffer.String()) } -// 抛出一个异常 +// Throw throws out an exception, which can be caught be TryCatch or recover. func Throw(exception interface{}) { panic(exception) } -// try...catch... +// TryCatch implements try...catch... logistics. func TryCatch(try func(), catch ... func(exception interface{})) { if len(catch) > 0 { defer func() { @@ -90,13 +78,9 @@ func TryCatch(try func(), catch ... func(exception interface{})) { try() } -// IsEmpty checks given value empty or not. -// false: integer(0), bool(false), slice/map(len=0), nil; -// true : other. -// -// 判断给定的变量是否为空。 -// 整型为0, 布尔为false, slice/map长度为0, 其他为nil的情况,都为空。 -// 为空时返回true,否则返回false。 +// IsEmpty checks given empty or not. +// It returns false if is: integer(0), bool(false), slice/map(len=0), nil; +// or else returns true. func IsEmpty(value interface{}) bool { return empty.IsEmpty(value) } diff --git a/g/util/gutil/gutil_comparator.go b/g/util/gutil/gutil_comparator.go new file mode 100644 index 000000000..f571ba4db --- /dev/null +++ b/g/util/gutil/gutil_comparator.go @@ -0,0 +1,103 @@ +package gutil + +import ( + "github.com/gogf/gf/g/util/gconv" + "strings" +) + +// Comparator is a function that compare a and b, and returns the result as int. +// +// Should return a number: +// negative , if a < b +// zero , if a == b +// positive , if a > b +type Comparator func(a, b interface{}) int + +// ComparatorString provides a fast comparison on strings. +func ComparatorString(a, b interface{}) int { + return strings.Compare(gconv.String(a), gconv.String(b)) +} + +// ComparatorInt provides a basic comparison on int. +func ComparatorInt(a, b interface{}) int { + return gconv.Int(a) - gconv.Int(b) +} + +// ComparatorInt8 provides a basic comparison on int8. +func ComparatorInt8(a, b interface{}) int { + return int(gconv.Int8(a) - gconv.Int8(b)) +} + +// ComparatorInt16 provides a basic comparison on int16. +func ComparatorInt16(a, b interface{}) int { + return int(gconv.Int16(a) - gconv.Int16(b)) +} + +// ComparatorInt32 provides a basic comparison on int32. +func ComparatorInt32(a, b interface{}) int { + return int(gconv.Int32(a) - gconv.Int32(b)) +} + +// ComparatorInt64 provides a basic comparison on int64. +func ComparatorInt64(a, b interface{}) int { + return int(gconv.Int64(a) - gconv.Int64(b)) +} + +// ComparatorUint provides a basic comparison on uint. +func ComparatorUint(a, b interface{}) int { + return int(gconv.Uint(a) - gconv.Uint(b)) +} + +// ComparatorUint8 provides a basic comparison on uint8. +func ComparatorUint8(a, b interface{}) int { + return int(gconv.Uint8(a) - gconv.Uint8(b)) +} + +// ComparatorUint16 provides a basic comparison on uint16. +func ComparatorUint16(a, b interface{}) int { + return int(gconv.Uint16(a) - gconv.Uint16(b)) +} + +// ComparatorUint32 provides a basic comparison on uint32. +func ComparatorUint32(a, b interface{}) int { + return int(gconv.Uint32(a) - gconv.Uint32(b)) +} + +// ComparatorUint64 provides a basic comparison on uint64. +func ComparatorUint64(a, b interface{}) int { + return int(gconv.Uint64(a) - gconv.Uint64(b)) +} + +// ComparatorFloat32 provides a basic comparison on float32. +func ComparatorFloat32(a, b interface{}) int { + return int(gconv.Float32(a) - gconv.Float32(b)) +} + +// ComparatorFloat64 provides a basic comparison on float64. +func ComparatorFloat64(a, b interface{}) int { + return int(gconv.Float64(a) - gconv.Float64(b)) +} + +// ComparatorByte provides a basic comparison on byte. +func ComparatorByte(a, b interface{}) int { + return int(gconv.Byte(a) - gconv.Byte(b)) +} + +// ComparatorRune provides a basic comparison on rune. +func ComparatorRune(a, b interface{}) int { + return int(gconv.Rune(a) - gconv.Rune(b)) +} + +// ComparatorTime provides a basic comparison on time.Time. +func ComparatorTime(a, b interface{}) int { + aTime := gconv.Time(a) + bTime := gconv.Time(b) + switch { + case aTime.After(bTime): + return 1 + case aTime.Before(bTime): + return -1 + default: + return 0 + } +} \ No newline at end of file diff --git a/g/util/gutil/gutil_test.go b/g/util/gutil/gutil_z_bench_test.go similarity index 100% rename from g/util/gutil/gutil_test.go rename to g/util/gutil/gutil_z_bench_test.go diff --git a/g/util/gvalid/gvalid_check.go b/g/util/gvalid/gvalid_check.go index 074c711fd..cd18389de 100644 --- a/g/util/gvalid/gvalid_check.go +++ b/g/util/gvalid/gvalid_check.go @@ -25,7 +25,7 @@ const ( var ( // 默认错误消息管理对象(并发安全) - errorMsgMap = gmap.NewStringStringMap() + errorMsgMap = gmap.NewStrStrMap() // 单规则正则对象,这里使用包内部变量存储,不需要多次解析 ruleRegex, _ = regexp.Compile(gSINGLE_RULE_PATTERN) diff --git a/g/util/gvalid/gvalid_message.go b/g/util/gvalid/gvalid_message.go index 614170c42..76d2bb0d1 100644 --- a/g/util/gvalid/gvalid_message.go +++ b/g/util/gvalid/gvalid_message.go @@ -56,7 +56,7 @@ var defaultMessages = map[string]string { // 初始化错误消息管理对象 func init() { - errorMsgMap.BatchSet(defaultMessages) + errorMsgMap.Sets(defaultMessages) } // 替换默认的错误提示为指定的自定义提示 @@ -64,5 +64,5 @@ func init() { // 1、便于多语言错误提示设置; // 2、默认错误提示信息不满意; func SetDefaultErrorMsgs(msgs map[string]string) { - errorMsgMap.BatchSet(msgs) + errorMsgMap.Sets(msgs) } \ No newline at end of file diff --git a/geg/container/gmap/gmap.go b/geg/container/gmap/gmap.go index ad8a9baf0..1f86e5c5e 100644 --- a/geg/container/gmap/gmap.go +++ b/geg/container/gmap/gmap.go @@ -18,7 +18,7 @@ func main() { // 查询大小 fmt.Println(m.Size()) // 批量设置键值对(不同的数据类型对象参数不同) - m.BatchSet(map[interface{}]interface{}{ + m.Sets(map[interface{}]interface{}{ 10: 10, 11: 11, }) @@ -35,7 +35,7 @@ func main() { fmt.Println(m.Size()) // 批量删除 - m.BatchRemove([]interface{}{10, 11}) + m.Removes([]interface{}{10, 11}) fmt.Println(m.Size()) // 当前键名列表(随机排序) @@ -44,10 +44,10 @@ func main() { fmt.Println(m.Values()) // 查询键名,当键值不存在时,写入给定的默认值 - fmt.Println(m.GetWithDefault(100, 100)) + fmt.Println(m.GetOrSet(100, 100)) // 删除键值对,并返回对应的键值 - fmt.Println(m.GetAndRemove(100)) + fmt.Println(m.Remove(100)) // 遍历map m.Iterator(func(k interface{}, v interface{}) bool { diff --git a/geg/container/gmap/gmap_map_safe.go b/geg/container/gmap/gmap_map_safe.go index f225d5473..63b8f2bf7 100644 --- a/geg/container/gmap/gmap_map_safe.go +++ b/geg/container/gmap/gmap_map_safe.go @@ -9,7 +9,7 @@ func main() { m := gmap.New() m.Set("1", "1") - m1 := m.Clone() + m1 := m.Map() m1["2"] = "2" g.Dump(m.Clone()) diff --git a/geg/container/gmap/gmap_order.go b/geg/container/gmap/gmap_order.go new file mode 100644 index 000000000..a41469f87 --- /dev/null +++ b/geg/container/gmap/gmap_order.go @@ -0,0 +1,30 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/util/gutil" +) + +func main() { + array := g.Slice{2, 3, 1, 5, 4, 6, 8, 7, 9} + hashMap := gmap.New(true) + linkMap := gmap.NewLinkMap(true) + treeMap := gmap.NewTreeMap(gutil.ComparatorInt, true) + for _, v := range array { + hashMap.Set(v, v) + } + for _, v := range array { + linkMap.Set(v, v) + } + for _, v := range array { + treeMap.Set(v, v) + } + fmt.Println("HashMap Keys:", hashMap.Keys()) + fmt.Println("HashMap Values:", hashMap.Values()) + fmt.Println("LinkMap Keys:", linkMap.Keys()) + fmt.Println("LinkMap Values:", linkMap.Values()) + fmt.Println("TreeMap Keys:", treeMap.Keys()) + fmt.Println("TreeMap Values:", treeMap.Values()) +} diff --git a/geg/container/gmap/gmap_treemap.go b/geg/container/gmap/gmap_treemap.go new file mode 100644 index 000000000..f66a8006d --- /dev/null +++ b/geg/container/gmap/gmap_treemap.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g/container/gmap" + "github.com/gogf/gf/g/util/gutil" +) + +func main() { + m := gmap.NewTreeMap(gutil.ComparatorInt) + + // 设置键值对 + for i := 0; i < 10; i++ { + m.Set(i, i) + } + // 查询大小 + fmt.Println(m.Size()) + // 批量设置键值对(不同的数据类型对象参数不同) + m.Sets(map[interface{}]interface{}{ + 10: 10, + 11: 11, + }) + fmt.Println(m.Size()) + + // 查询是否存在 + fmt.Println(m.Contains(1)) + + // 查询键值 + fmt.Println(m.Get(1)) + + // 删除数据项 + m.Remove(9) + fmt.Println(m.Size()) + + // 批量删除 + m.Removes([]interface{}{10, 11}) + fmt.Println(m.Size()) + + // 当前键名列表(随机排序) + fmt.Println(m.Keys()) + // 当前键值列表(随机排序) + fmt.Println(m.Values()) + + // 查询键名,当键值不存在时,写入给定的默认值 + fmt.Println(m.GetOrSet(100, 100)) + + // 删除键值对,并返回对应的键值 + fmt.Println(m.Remove(100)) + + // 遍历map + m.IteratorAsc(func(k interface{}, v interface{}) bool { + fmt.Printf("%v:%v ", k, v) + return true + }) + fmt.Println() + + // 清空map + m.Clear() + + // 判断map是否为空 + fmt.Println(m.IsEmpty()) +} diff --git a/geg/container/gtree/gtree_avltree.go b/geg/container/gtree/gtree_avltree.go new file mode 100644 index 000000000..ef99b43e6 --- /dev/null +++ b/geg/container/gtree/gtree_avltree.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g/container/gtree" + "github.com/gogf/gf/g/util/gutil" +) + +func main() { + tree := gtree.NewAVLTree(gutil.ComparatorInt) + for i := 0; i < 10; i++ { + tree.Set(i, i*10) + } + // 打印树形 + tree.Print() + // 前序遍历 + fmt.Println("ASC:") + tree.IteratorAsc(func(key, value interface{}) bool { + fmt.Println(key, value) + return true + }) + // 后续遍历 + fmt.Println("DESC:") + tree.IteratorDesc(func(key, value interface{}) bool { + fmt.Println(key, value) + return true + }) +} \ No newline at end of file diff --git a/geg/container/gtree/gtree_btree.go b/geg/container/gtree/gtree_btree.go new file mode 100644 index 000000000..01c001f4d --- /dev/null +++ b/geg/container/gtree/gtree_btree.go @@ -0,0 +1,21 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g/container/gtree" +) + +func main() { + tree := gtree.NewBTree(10, func(v1, v2 interface{}) int { + return v1.(int) - v2.(int) + }) + for i := 0; i < 20; i++ { + tree.Set(i, i*10) + } + fmt.Println(tree.String()) + + tree.IteratorDesc(func(key, value interface{}) bool { + fmt.Println(key, value) + return true + }) +} \ No newline at end of file diff --git a/geg/container/gtree/gtree_redblackmap.go b/geg/container/gtree/gtree_redblackmap.go new file mode 100644 index 000000000..b95a16ec0 --- /dev/null +++ b/geg/container/gtree/gtree_redblackmap.go @@ -0,0 +1,62 @@ +package main + +import ( + "fmt" + "github.com/gogf/gf/g/container/gtree" + "github.com/gogf/gf/g/util/gutil" +) + +func main() { + m := gtree.NewRedBlackTree(gutil.ComparatorInt) + + // 设置键值对 + for i := 0; i < 10; i++ { + m.Set(i, i*10) + } + // 查询大小 + fmt.Println(m.Size()) + // 批量设置键值对(不同的数据类型对象参数不同) + m.Sets(map[interface{}]interface{}{ + 10: 10, + 11: 11, + }) + fmt.Println(m.Size()) + + // 查询是否存在 + fmt.Println(m.Contains(1)) + + // 查询键值 + fmt.Println(m.Get(1)) + + // 删除数据项 + m.Remove(9) + fmt.Println(m.Size()) + + // 批量删除 + m.Removes([]interface{}{10, 11}) + fmt.Println(m.Size()) + + // 当前键名列表(随机排序) + fmt.Println(m.Keys()) + // 当前键值列表(随机排序) + fmt.Println(m.Values()) + + // 查询键名,当键值不存在时,写入给定的默认值 + fmt.Println(m.GetOrSet(100, 100)) + + // 删除键值对,并返回对应的键值 + fmt.Println(m.Remove(100)) + + // 遍历map + m.IteratorAsc(func(k interface{}, v interface{}) bool { + fmt.Printf("%v:%v ", k, v) + return true + }) + fmt.Println() + + // 清空map + m.Clear() + + // 判断map是否为空 + fmt.Println(m.IsEmpty()) +} diff --git a/geg/container/gtree/gtree_redblacktree.go b/geg/container/gtree/gtree_redblacktree.go new file mode 100644 index 000000000..6691c1a4d --- /dev/null +++ b/geg/container/gtree/gtree_redblacktree.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/gogf/gf/g/container/gtree" +) + +func main() { + tree := gtree.NewRedBlackTree(func(v1, v2 interface{}) int { + return v1.(int) - v2.(int) + }) + for i := 0; i < 10; i++ { + tree.Set(i, i) + } + tree.Print() + tree.Flip() + tree.Print() +} \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index 8cb13c441..7e80fb035 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,9 +1,40 @@ package main import ( + "encoding/json" "fmt" + "time" ) func main() { - fmt.Println("10" > "4") + //b, e := gjson.MarshalOrdered(g.Map{ + // "a" : 1, + // "b" : 2, + // "c" : 3, + //}) + //fmt.Println(e) + //fmt.Println(string(b)) + + //m := map[string]interface{}{ + // "facet_is_special_price":[]string{"1"}, + // "score_outlet":"0", + // "skus":[]string{"DI139BE71WDWDFMX", "DI139BE71WDWDFMX-519406"}, + // "facet_novelty_two_days":[]string{"0"}, + // "facet_brand":[]string{"139"}, + // "sku":[]string{"DI139BE71WDWDFMX"}, + //} + + for { + m := make(map[string]interface{}) + m["facet_is_special_price"] = []string{"1"} + m["score_outlet"] = "0" + m["skus"] = []string{"DI139BE71WDWDFMX", "DI139BE71WDWDFMX-519406"} + m["facet_novelty_two_days"] = []string{"0"} + m["facet_brand"] = []string{"139"} + m["sku"] = []string{"DI139BE71WDWDFMX"} + b, _ := json.Marshal(m) + fmt.Println(string(b)) + time.Sleep(100*time.Millisecond) + } + } \ No newline at end of file diff --git a/version.go b/version.go index daa93f84d..834684767 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.6.9" +const VERSION = "v1.6.11" const AUTHORS = "john"