Merge branch 'master' into master

This commit is contained in:
John Guo
2020-04-23 22:57:23 +08:00
committed by GitHub
152 changed files with 5943 additions and 2857 deletions

View File

@ -1,23 +1,12 @@
package main
import (
"encoding/json"
"encoding/hex"
"fmt"
"gopkg.in/yaml.v3"
)
func main() {
data := []byte(`
m:
k: v
`)
var result map[string]interface{}
if err := yaml.Unmarshal(data, &result); err != nil {
panic(err)
}
b, err := json.Marshal(result)
if err != nil {
panic(err)
}
b := []byte{3, 0, 0}
fmt.Println(string(b))
fmt.Println(hex.EncodeToString(b))
}

View File

@ -1,6 +1,8 @@
language: go
go:
- "1.11.x"
- "1.12.x"
- "1.13.x"
- "1.14.x"

View File

@ -71,6 +71,8 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|[侯哥](http://www.macnie.com)|wechat|¥10.00|
|如果🍋|alipay|¥100.00| 错过的奶茶^_^
|蔡蔡|wechat|¥666.00| gf真强大让项目省心
|jack|wechat|¥100.00|
|sbilly|wechat|¥100.00| 祝好!
<img src="https://goframe.org/images/donate.png"/>

View File

@ -9,7 +9,7 @@
English | [简体中文](README_ZH.MD)
`GF(GoFrame)` is a modular, full-featured and production-ready application development framework
`GF(GoFrame)` is a modular, high-performance and production-ready application development framework
of golang. Providing a series of core components and dozens of practical modules, such as:
cache, logging, containers, timer, resource, validator, database orm, etc.
Supporting web server integrated with router, cookie, session, middleware, logger, configure,
@ -27,9 +27,67 @@ require github.com/gogf/gf latest
# Limitation
```
golang version >= 1.13
golang version >= 1.11
```
# Architecture
<div align=center>
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# Performance
Here's the most popular Golang frameworks and libraries performance testing result in `WEB Server`. Performance testing cases source codes are hosted at: https://github.com/gogf/gf-performance
## Environment
OS : Ubuntu 18.04 amd64
CPU : AMD A8-6600K x 4
MEM : 32GB
GO : v1.13.4
## Testing Tool
`ab`: Apache HTTP server benchmarking tool.
Command:
```
ab -t 10 -c 100 http://127.0.0.1:3000/hello
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
ab -t 10 -c 100 http://127.0.0.1:3000/json
```
The concurrency starts from `100` to `10000`.
> Run `5` times for each case of each project and pick up the best testing result.
## 1. Hello World
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
</tr>
</table>
## 2. Json Response
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
</tr>
</table>
# Documentation
* 中文官网: https://goframe.org
@ -43,12 +101,6 @@ golang version >= 1.13
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
# Architecture
<div align=center>
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# License
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.

View File

@ -41,7 +41,7 @@ require github.com/gogf/gf latest
# 限制
```shell
golang版本 >= 1.13
golang版本 >= 1.11
```
# 架构
@ -49,6 +49,59 @@ golang版本 >= 1.13
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# 性能
以下是目前最流行的`WEB Server` Golang框架/类库性能测试结果。
性能测试用例源代码仓库: https://github.com/gogf/gf-performance
## 环境:
OS : Ubuntu 18.04 amd64
CPU : AMD A8-6600K x 4
MEM : 32GB
GO : v1.13.4
## 工具
`ab`: Apache HTTP server benchmarking tool.
测试命令:
```
ab -t 10 -c 100 http://127.0.0.1:3000/hello
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
ab -t 10 -c 100 http://127.0.0.1:3000/json
```
并发客户端数量从 `100` 递增到 `10000`。
> 每个项目的每个用例均运行`5`次,取最优的结果展示。
## 1. Hello World
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
</tr>
</table>
## 2. Json Response
<table>
<tr>
<th>Throughputs</th>
<th>Mean Latency</th>
<th>P99 Latency</th>
</tr>
<tr>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
</tr>
</table>
# 文档

View File

@ -8,6 +8,12 @@ package garray
import "strings"
// apiInterfaces is used for type assert api for Interfaces.
type apiInterfaces interface {
Interfaces() []interface{}
}
// defaultComparatorInt for int comparison.
func defaultComparatorInt(a, b int) int {
if a < b {
return -1
@ -18,6 +24,7 @@ func defaultComparatorInt(a, b int) int {
return 0
}
// defaultComparatorStr for string comparison.
func defaultComparatorStr(a, b string) int {
return strings.Compare(a, b)
}

View File

@ -23,7 +23,7 @@ import (
// Array is a golang array with rich features.
type Array struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []interface{}
}
@ -44,7 +44,7 @@ func NewArray(safe ...bool) *Array {
// which is false in default.
func NewArraySize(size int, cap int, safe ...bool) *Array {
return &Array{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]interface{}, size, cap),
}
}
@ -79,7 +79,7 @@ func NewFromCopy(array []interface{}, safe ...bool) *Array {
// which is false in default.
func NewArrayFrom(array []interface{}, safe ...bool) *Array {
return &Array{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: array,
}
}
@ -91,7 +91,7 @@ func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
newArray := make([]interface{}, len(array))
copy(newArray, array)
return &Array{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: newArray,
}
}
@ -533,23 +533,7 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *Array) Merge(array interface{}) *Array {
switch v := array.(type) {
case *Array:
a.Append(gconv.Interfaces(v.Slice())...)
case *IntArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *StrArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *SortedArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *SortedIntArray:
a.Append(gconv.Interfaces(v.Slice())...)
case *SortedStrArray:
a.Append(gconv.Interfaces(v.Slice())...)
default:
a.Append(gconv.Interfaces(array)...)
}
return a
return a.Append(gconv.Interfaces(array)...)
}
// Fill fills an array with num entries of the value <value>,
@ -668,6 +652,9 @@ func (a *Array) Reverse() *Array {
func (a *Array) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
@ -694,7 +681,7 @@ func (a *Array) Iterator(f func(k int, v interface{}) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
a.mu.RLock()
@ -706,7 +693,7 @@ func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
a.mu.RLock()
@ -749,8 +736,7 @@ func (a *Array) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *Array) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.array == nil {
a.array = make([]interface{}, 0)
}
a.mu.Lock()
@ -763,9 +749,6 @@ func (a *Array) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *Array) UnmarshalValue(value interface{}) error {
if a.mu == nil {
a.mu = rwmutex.New()
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
@ -806,6 +789,16 @@ func (a *Array) FilterEmpty() *Array {
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *Array) IsEmpty() bool {
return a.Len() == 0

View File

@ -21,7 +21,7 @@ import (
// IntArray is a golang int array with rich features.
type IntArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []int
}
@ -37,7 +37,7 @@ func NewIntArray(safe ...bool) *IntArray {
// which is false in default.
func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
return &IntArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]int, size, cap),
}
}
@ -62,7 +62,7 @@ func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
// which is false in default.
func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
return &IntArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: array,
}
}
@ -74,7 +74,7 @@ func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
newArray := make([]int, len(array))
copy(newArray, array)
return &IntArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: newArray,
}
}
@ -535,23 +535,7 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *IntArray) Merge(array interface{}) *IntArray {
switch v := array.(type) {
case *Array:
a.Append(gconv.Ints(v.Slice())...)
case *IntArray:
a.Append(gconv.Ints(v.Slice())...)
case *StrArray:
a.Append(gconv.Ints(v.Slice())...)
case *SortedArray:
a.Append(gconv.Ints(v.Slice())...)
case *SortedIntArray:
a.Append(gconv.Ints(v.Slice())...)
case *SortedStrArray:
a.Append(gconv.Ints(v.Slice())...)
default:
a.Append(gconv.Ints(array)...)
}
return a
return a.Append(gconv.Ints(array)...)
}
// Fill fills an array with num entries of the value <value>,
@ -670,6 +654,9 @@ func (a *IntArray) Reverse() *IntArray {
func (a *IntArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
@ -696,7 +683,7 @@ func (a *IntArray) Iterator(f func(k int, v int) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
a.mu.RLock()
@ -708,7 +695,7 @@ func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *IntArray) IteratorDesc(f func(k int, v int) bool) {
a.mu.RLock()
@ -734,8 +721,7 @@ func (a *IntArray) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *IntArray) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.array == nil {
a.array = make([]int, 0)
}
a.mu.Lock()
@ -748,9 +734,6 @@ func (a *IntArray) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *IntArray) UnmarshalValue(value interface{}) error {
if a.mu == nil {
a.mu = rwmutex.New()
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
@ -776,6 +759,16 @@ func (a *IntArray) FilterEmpty() *IntArray {
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *IntArray) Walk(f func(value int) int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *IntArray) IsEmpty() bool {
return a.Len() == 0

View File

@ -23,7 +23,7 @@ import (
// StrArray is a golang string array with rich features.
type StrArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []string
}
@ -39,7 +39,7 @@ func NewStrArray(safe ...bool) *StrArray {
// which is false in default.
func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
return &StrArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]string, size, cap),
}
}
@ -49,7 +49,7 @@ func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
// which is false in default.
func NewStrArrayFrom(array []string, safe ...bool) *StrArray {
return &StrArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: array,
}
}
@ -61,7 +61,7 @@ func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray {
newArray := make([]string, len(array))
copy(newArray, array)
return &StrArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: newArray,
}
}
@ -526,23 +526,7 @@ func (a *StrArray) RLockFunc(f func(array []string)) *StrArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *StrArray) Merge(array interface{}) *StrArray {
switch v := array.(type) {
case *Array:
a.Append(gconv.Strings(v.Slice())...)
case *IntArray:
a.Append(gconv.Strings(v.Slice())...)
case *StrArray:
a.Append(gconv.Strings(v.Slice())...)
case *SortedArray:
a.Append(gconv.Strings(v.Slice())...)
case *SortedIntArray:
a.Append(gconv.Strings(v.Slice())...)
case *SortedStrArray:
a.Append(gconv.Strings(v.Slice())...)
default:
a.Append(gconv.Strings(array)...)
}
return a
return a.Append(gconv.Strings(array)...)
}
// Fill fills an array with num entries of the value <value>,
@ -661,6 +645,9 @@ func (a *StrArray) Reverse() *StrArray {
func (a *StrArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(v)
@ -687,7 +674,7 @@ func (a *StrArray) Iterator(f func(k int, v string) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
a.mu.RLock()
@ -699,7 +686,7 @@ func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *StrArray) IteratorDesc(f func(k int, v string) bool) {
a.mu.RLock()
@ -736,8 +723,7 @@ func (a *StrArray) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *StrArray) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.array == nil {
a.array = make([]string, 0)
}
a.mu.Lock()
@ -750,9 +736,6 @@ func (a *StrArray) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *StrArray) UnmarshalValue(value interface{}) error {
if a.mu == nil {
a.mu = rwmutex.New()
}
a.mu.Lock()
defer a.mu.Unlock()
switch value.(type) {
@ -778,6 +761,16 @@ func (a *StrArray) FilterEmpty() *StrArray {
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *StrArray) Walk(f func(value string) string) *StrArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *StrArray) IsEmpty() bool {
return a.Len() == 0

View File

@ -16,7 +16,6 @@ import (
"math"
"sort"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/grand"
@ -25,9 +24,9 @@ import (
// SortedArray is a golang sorted array with rich features.
// It's using increasing order in default.
type SortedArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []interface{}
unique *gtype.Bool // Whether enable unique feature(false)
unique bool // Whether enable unique feature(false)
comparator func(a, b interface{}) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
}
@ -46,8 +45,7 @@ func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *Sorted
// which is false in default.
func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
return &SortedArray{
mu: rwmutex.New(safe...),
unique: gtype.NewBool(),
mu: rwmutex.Create(safe...),
array: make([]interface{}, 0, cap),
comparator: comparator,
}
@ -75,7 +73,7 @@ func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) i
a := NewSortedArraySize(0, comparator, safe...)
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
return a.getComparator()(a.array[i], a.array[j]) < 0
})
return a
}
@ -95,19 +93,19 @@ 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.comparator(a.array[i], a.array[j]) < 0
return a.getComparator()(a.array[i], a.array[j]) < 0
})
return a
}
// SetComparator sets/changes the comparator for sorting.
// It resorts the array as the comparator is changed.
func (a *SortedArray) SetComparator(comparator func(a, b interface{}) int) {
a.mu.Lock()
defer a.mu.Unlock()
a.comparator = comparator
// Resort the array if comparator is changed.
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
return a.getComparator()(a.array[i], a.array[j]) < 0
})
}
@ -118,7 +116,7 @@ func (a *SortedArray) Sort() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return a.comparator(a.array[i], a.array[j]) < 0
return a.getComparator()(a.array[i], a.array[j]) < 0
})
return a
}
@ -132,7 +130,7 @@ func (a *SortedArray) Add(values ...interface{}) *SortedArray {
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
if a.unique && cmp == 0 {
continue
}
if index < 0 {
@ -390,7 +388,7 @@ func (a *SortedArray) Len() int {
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
// or else a pointer to the underlying data.
func (a *SortedArray) Slice() []interface{} {
array := ([]interface{})(nil)
var array []interface{}
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
@ -439,8 +437,8 @@ func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result
mid := 0
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
mid = (min + max) / 2
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
@ -457,8 +455,8 @@ func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedArray) SetUnique(unique bool) *SortedArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
@ -477,7 +475,7 @@ func (a *SortedArray) Unique() *SortedArray {
if i == len(a.array)-1 {
break
}
if a.comparator(a.array[i], a.array[i+1]) == 0 {
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
} else {
i++
@ -509,6 +507,12 @@ func (a *SortedArray) Clear() *SortedArray {
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer sort.Slice(a.array, func(i, j int) bool {
return a.getComparator()(a.array[i], a.array[j]) < 0
})
f(a.array)
return a
}
@ -526,23 +530,7 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedArray) Merge(array interface{}) *SortedArray {
switch v := array.(type) {
case *Array:
a.Add(gconv.Interfaces(v.Slice())...)
case *IntArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *StrArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *SortedArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *SortedIntArray:
a.Add(gconv.Interfaces(v.Slice())...)
case *SortedStrArray:
a.Add(gconv.Interfaces(v.Slice())...)
default:
a.Add(gconv.Interfaces(array)...)
}
return a
return a.Add(gconv.Interfaces(array)...)
}
// Chunk splits an array into multiple arrays,
@ -596,6 +584,9 @@ func (a *SortedArray) Rands(size int) []interface{} {
func (a *SortedArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
@ -622,7 +613,7 @@ func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
a.mu.RLock()
@ -634,7 +625,7 @@ func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) {
a.mu.RLock()
@ -676,12 +667,10 @@ func (a *SortedArray) MarshalJSON() ([]byte, error) {
}
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
// Note that the comparator is set as string comparator in default.
func (a *SortedArray) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.comparator == nil {
a.array = make([]interface{}, 0)
a.unique = gtype.NewBool()
// Note that the comparator is string comparator in default.
a.comparator = gutil.ComparatorString
}
a.mu.Lock()
@ -698,11 +687,9 @@ func (a *SortedArray) UnmarshalJSON(b []byte) error {
}
// UnmarshalValue is an interface implement which sets any type of value for array.
// Note that the comparator is set as string comparator in default.
func (a *SortedArray) UnmarshalValue(value interface{}) (err error) {
if a.mu == nil {
a.mu = rwmutex.New()
a.unique = gtype.NewBool()
// Note that the comparator is string comparator in default.
if a.comparator == nil {
a.comparator = gutil.ComparatorString
}
a.mu.Lock()
@ -764,7 +751,30 @@ func (a *SortedArray) FilterEmpty() *SortedArray {
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *SortedArray) Walk(f func(value interface{}) interface{}) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer sort.Slice(a.array, func(i, j int) bool {
return a.getComparator()(a.array[i], a.array[j]) < 0
})
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *SortedArray) IsEmpty() bool {
return a.Len() == 0
}
// getComparator returns the comparator if it's previously set,
// or else it panics.
func (a *SortedArray) getComparator() func(a, b interface{}) int {
if a.comparator == nil {
panic("comparator is missing for sorted array")
}
return a.comparator
}

View File

@ -13,7 +13,6 @@ import (
"math"
"sort"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/grand"
@ -22,9 +21,9 @@ import (
// SortedIntArray is a golang sorted int array with rich features.
// It's using increasing order in default.
type SortedIntArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []int
unique *gtype.Bool // Whether enable unique feature(false)
unique bool // Whether enable unique feature(false)
comparator func(a, b int) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
}
@ -48,9 +47,8 @@ func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *S
// which is false in default.
func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
return &SortedIntArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]int, 0, cap),
unique: gtype.NewBool(),
comparator: defaultComparatorInt,
}
}
@ -94,7 +92,7 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Ints(a.array)
quickSortInt(a.array, a.getComparator())
return a
}
@ -104,7 +102,7 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
func (a *SortedIntArray) Sort() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Ints(a.array)
quickSortInt(a.array, a.getComparator())
return a
}
@ -117,7 +115,7 @@ func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
if a.unique && cmp == 0 {
continue
}
if index < 0 {
@ -436,8 +434,8 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
mid := 0
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
mid = (min + max) / 2
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
@ -454,8 +452,8 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
@ -474,7 +472,7 @@ func (a *SortedIntArray) Unique() *SortedIntArray {
if i == len(a.array)-1 {
break
}
if a.comparator(a.array[i], a.array[i+1]) == 0 {
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
} else {
i++
@ -523,23 +521,7 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
switch v := array.(type) {
case *Array:
a.Add(gconv.Ints(v.Slice())...)
case *IntArray:
a.Add(gconv.Ints(v.Slice())...)
case *StrArray:
a.Add(gconv.Ints(v.Slice())...)
case *SortedArray:
a.Add(gconv.Ints(v.Slice())...)
case *SortedIntArray:
a.Add(gconv.Ints(v.Slice())...)
case *SortedStrArray:
a.Add(gconv.Ints(v.Slice())...)
default:
a.Add(gconv.Ints(array)...)
}
return a
return a.Add(gconv.Ints(array)...)
}
// Chunk splits an array into multiple arrays,
@ -593,6 +575,9 @@ func (a *SortedIntArray) Rands(size int) []int {
func (a *SortedIntArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(gconv.String(v))
@ -619,7 +604,7 @@ func (a *SortedIntArray) Iterator(f func(k int, v int) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
a.mu.RLock()
@ -631,7 +616,7 @@ func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) {
a.mu.RLock()
@ -657,10 +642,8 @@ func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.comparator == nil {
a.array = make([]int, 0)
a.unique = gtype.NewBool()
a.comparator = defaultComparatorInt
}
a.mu.Lock()
@ -676,10 +659,7 @@ func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
if a.mu == nil {
a.mu = rwmutex.New()
a.unique = gtype.NewBool()
// Note that the comparator is string comparator in default.
if a.comparator == nil {
a.comparator = defaultComparatorInt
}
a.mu.Lock()
@ -717,7 +697,30 @@ func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *SortedIntArray) Walk(f func(value int) int) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer quickSortInt(a.array, a.getComparator())
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *SortedIntArray) IsEmpty() bool {
return a.Len() == 0
}
// getComparator returns the comparator if it's previously set,
// or else it returns a default comparator.
func (a *SortedIntArray) getComparator() func(a, b int) int {
if a.comparator == nil {
return defaultComparatorInt
}
return a.comparator
}

View File

@ -13,7 +13,6 @@ import (
"math"
"sort"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/grand"
@ -22,9 +21,9 @@ import (
// SortedStrArray is a golang sorted string array with rich features.
// It's using increasing order in default.
type SortedStrArray struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
array []string
unique *gtype.Bool // Whether enable unique feature(false)
unique bool // Whether enable unique feature(false)
comparator func(a, b string) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
}
@ -48,9 +47,8 @@ func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool)
// which is false in default.
func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
return &SortedStrArray{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
array: make([]string, 0, cap),
unique: gtype.NewBool(),
comparator: defaultComparatorStr,
}
}
@ -61,7 +59,7 @@ func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray {
a := NewSortedStrArraySize(0, safe...)
a.array = array
quickSortStr(a.array, a.comparator)
quickSortStr(a.array, a.getComparator())
return a
}
@ -79,7 +77,7 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
quickSortStr(a.array, a.comparator)
quickSortStr(a.array, a.getComparator())
return a
}
@ -89,7 +87,7 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
func (a *SortedStrArray) Sort() *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
quickSortStr(a.array, a.comparator)
quickSortStr(a.array, a.getComparator())
return a
}
@ -102,7 +100,7 @@ func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
defer a.mu.Unlock()
for _, value := range values {
index, cmp := a.binSearch(value, false)
if a.unique.Val() && cmp == 0 {
if a.unique && cmp == 0 {
continue
}
if index < 0 {
@ -421,8 +419,8 @@ func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result i
mid := 0
cmp := -2
for min <= max {
mid = int((min + max) / 2)
cmp = a.comparator(value, a.array[mid])
mid = (min + max) / 2
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:
max = mid - 1
@ -439,8 +437,8 @@ func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result i
// which means it does not contain any repeated items.
// It also do unique check, remove all repeated items.
func (a *SortedStrArray) SetUnique(unique bool) *SortedStrArray {
oldUnique := a.unique.Val()
a.unique.Set(unique)
oldUnique := a.unique
a.unique = unique
if unique && oldUnique != unique {
a.Unique()
}
@ -459,7 +457,7 @@ func (a *SortedStrArray) Unique() *SortedStrArray {
if i == len(a.array)-1 {
break
}
if a.comparator(a.array[i], a.array[i+1]) == 0 {
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
} else {
i++
@ -508,23 +506,7 @@ func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
// The difference between Merge and Append is Append supports only specified slice type,
// but Merge supports more parameter types.
func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray {
switch v := array.(type) {
case *Array:
a.Add(gconv.Strings(v.Slice())...)
case *IntArray:
a.Add(gconv.Strings(v.Slice())...)
case *StrArray:
a.Add(gconv.Strings(v.Slice())...)
case *SortedArray:
a.Add(gconv.Strings(v.Slice())...)
case *SortedIntArray:
a.Add(gconv.Strings(v.Slice())...)
case *SortedStrArray:
a.Add(gconv.Strings(v.Slice())...)
default:
a.Add(gconv.Strings(array)...)
}
return a
return a.Add(gconv.Strings(array)...)
}
// Chunk splits an array into multiple arrays,
@ -578,6 +560,9 @@ func (a *SortedStrArray) Rands(size int) []string {
func (a *SortedStrArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
if len(a.array) == 0 {
return ""
}
buffer := bytes.NewBuffer(nil)
for k, v := range a.array {
buffer.WriteString(v)
@ -604,7 +589,7 @@ func (a *SortedStrArray) Iterator(f func(k int, v string) bool) {
a.IteratorAsc(f)
}
// IteratorAsc iterates the array in ascending order with given callback function <f>.
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
a.mu.RLock()
@ -616,7 +601,7 @@ func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
}
}
// IteratorDesc iterates the array in descending order with given callback function <f>.
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) {
a.mu.RLock()
@ -653,10 +638,8 @@ func (a *SortedStrArray) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
if a.mu == nil {
a.mu = rwmutex.New()
if a.comparator == nil {
a.array = make([]string, 0)
a.unique = gtype.NewBool()
a.comparator = defaultComparatorStr
}
a.mu.Lock()
@ -672,10 +655,7 @@ func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for array.
func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
if a.mu == nil {
a.mu = rwmutex.New()
a.unique = gtype.NewBool()
// Note that the comparator is string comparator in default.
if a.comparator == nil {
a.comparator = defaultComparatorStr
}
a.mu.Lock()
@ -713,7 +693,30 @@ func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
return a
}
// Walk applies a user supplied function <f> to every item of array.
func (a *SortedStrArray) Walk(f func(value string) string) *SortedStrArray {
a.mu.Lock()
defer a.mu.Unlock()
// Keep the array always sorted.
defer quickSortStr(a.array, a.getComparator())
for i, v := range a.array {
a.array[i] = f(v)
}
return a
}
// IsEmpty checks whether the array is empty.
func (a *SortedStrArray) IsEmpty() bool {
return a.Len() == 0
}
// getComparator returns the comparator if it's previously set,
// or else it returns a default comparator.
func (a *SortedStrArray) getComparator() func(a, b string) int {
if a.comparator == nil {
return defaultComparatorStr
}
return a.comparator
}

View File

@ -9,6 +9,7 @@
package garray_test
import (
"github.com/gogf/gf/util/gutil"
"strings"
"testing"
@ -17,6 +18,55 @@ import (
"github.com/gogf/gf/util/gconv"
)
func Test_Array_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var array garray.Array
expect := []int{2, 3, 1}
array.Append(2, 3, 1)
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.IntArray
expect := []int{2, 3, 1}
array.Append(2, 3, 1)
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.StrArray
expect := []string{"b", "a"}
array.Append("b", "a")
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.SortedArray
array.SetComparator(gutil.ComparatorInt)
expect := []int{1, 2, 3}
array.Add(2, 3, 1)
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.SortedIntArray
expect := []int{1, 2, 3}
array.Add(2, 3, 1)
t.Assert(array.Slice(), expect)
})
gtest.C(t, func(t *gtest.T) {
var array garray.SortedStrArray
expect := []string{"a", "b", "c"}
array.Add("c", "a", "b")
t.Assert(array.Slice(), expect)
})
}
func Test_SortedIntArray_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var array garray.SortedIntArray
expect := []int{1, 2, 3}
array.Add(2, 3, 1)
t.Assert(array.Slice(), expect)
})
}
func Test_IntArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []int{1, 2, 3, 4, 5, 6}

View File

@ -650,3 +650,12 @@ func TestArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
}
func TestArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewArrayFrom(g.Slice{"1", "2"})
t.Assert(array.Walk(func(value interface{}) interface{} {
return "key-" + gconv.String(value)
}), g.Slice{"key-1", "key-2"})
})
}

View File

@ -683,3 +683,12 @@ func TestIntArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
}
func TestIntArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewIntArrayFrom(g.SliceInt{1, 2})
t.Assert(array.Walk(func(value int) int {
return 10 + value
}), g.Slice{11, 12})
})
}

View File

@ -671,3 +671,12 @@ func TestStrArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
})
}
func TestStrArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
t.Assert(array.Walk(func(value string) string {
return "key-" + value
}), g.Slice{"key-1", "key-2"})
})
}

View File

@ -779,3 +779,12 @@ func TestSortedArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
})
}
func TestSortedArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedArrayFrom(g.Slice{"1", "2"}, gutil.ComparatorString)
t.Assert(array.Walk(func(value interface{}) interface{} {
return "key-" + gconv.String(value)
}), g.Slice{"key-1", "key-2"})
})
}

View File

@ -642,3 +642,12 @@ func TestSortedIntArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
})
}
func TestSortedIntArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2})
t.Assert(array.Walk(func(value int) int {
return 10 + value
}), g.Slice{11, 12})
})
}

View File

@ -652,3 +652,12 @@ func TestSortedStrArray_FilterEmpty(t *testing.T) {
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
})
}
func TestSortedStrArray_Walk(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
t.Assert(array.Walk(func(value string) string {
return "key-" + value
}), g.Slice{"key-1", "key-2"})
})
}

View File

@ -20,8 +20,8 @@ import (
type (
List struct {
mu *rwmutex.RWMutex
list *list.List
mu rwmutex.RWMutex
list list.List
}
Element = list.Element
@ -30,8 +30,8 @@ type (
// New creates and returns a new empty doubly linked list.
func New(safe ...bool) *List {
return &List{
mu: rwmutex.New(safe...),
list: list.New(),
mu: rwmutex.Create(safe...),
list: list.List{},
}
}
@ -39,12 +39,12 @@ func New(safe ...bool) *List {
// The parameter <safe> is used to specify whether using list in concurrent-safety,
// which is false in default.
func NewFrom(array []interface{}, safe ...bool) *List {
l := list.New()
l := list.List{}
for _, v := range array {
l.PushBack(v)
}
return &List{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
list: l,
}
}
@ -273,7 +273,7 @@ func (l *List) PushBackList(other *List) {
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushBackList(other.list)
l.list.PushBackList(&other.list)
l.mu.Unlock()
}
@ -285,7 +285,7 @@ func (l *List) PushFrontList(other *List) {
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushFrontList(other.list)
l.list.PushFrontList(&other.list)
l.mu.Unlock()
}
@ -332,7 +332,7 @@ func (l *List) Removes(es []*Element) {
// RemoveAll removes all elements from list <l>.
func (l *List) RemoveAll() {
l.mu.Lock()
l.list = list.New()
l.list = list.List{}
l.mu.Unlock()
}
@ -345,14 +345,14 @@ func (l *List) Clear() {
func (l *List) RLockFunc(f func(list *list.List)) {
l.mu.RLock()
defer l.mu.RUnlock()
f(l.list)
f(&l.list)
}
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
func (l *List) LockFunc(f func(list *list.List)) {
l.mu.Lock()
defer l.mu.Unlock()
f(l.list)
f(&l.list)
}
// Iterator is alias of IteratorAsc.
@ -360,7 +360,7 @@ func (l *List) Iterator(f func(e *Element) bool) {
l.IteratorAsc(f)
}
// IteratorAsc iterates the list in ascending order with given callback function <f>.
// IteratorAsc iterates the list readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorAsc(f func(e *Element) bool) {
l.mu.RLock()
@ -375,7 +375,7 @@ func (l *List) IteratorAsc(f func(e *Element) bool) {
}
}
// IteratorDesc iterates the list in descending order with given callback function <f>.
// IteratorDesc iterates the list readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (l *List) IteratorDesc(f func(e *Element) bool) {
l.mu.RLock()
@ -425,10 +425,6 @@ func (l *List) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (l *List) UnmarshalJSON(b []byte) error {
if l.mu == nil {
l.mu = rwmutex.New()
l.list = list.New()
}
l.mu.Lock()
defer l.mu.Unlock()
var array []interface{}
@ -441,10 +437,6 @@ func (l *List) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for list.
func (l *List) UnmarshalValue(value interface{}) (err error) {
if l.mu == nil {
l.mu = rwmutex.New()
l.list = list.New()
}
l.mu.Lock()
defer l.mu.Unlock()
var array []interface{}

View File

@ -41,6 +41,44 @@ func checkListPointers(t *gtest.T, l *List, es []*Element) {
})
}
func TestVar(t *testing.T) {
var l List
l.PushFront(1)
l.PushFront(2)
if v := l.PopBack(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
l.PushBack(1)
l.PushBack(2)
if v := l.PopFront(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
}
func TestBasic(t *testing.T) {
l := New()
l.PushFront(1)

View File

@ -7,9 +7,10 @@
// Package gmap provides concurrent-safe/unsafe map containers.
package gmap
// Map based on hash table, alias of AnyAnyMap.
type Map = AnyAnyMap
type HashMap = AnyAnyMap
type (
Map = AnyAnyMap // Map is alias of AnyAnyMap.
HashMap = AnyAnyMap // HashMap is alias of AnyAnyMap.
)
// New creates and returns an empty hash map.
// The parameter <safe> is used to specify whether using map in concurrent-safety,

View File

@ -18,7 +18,7 @@ import (
)
type AnyAnyMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[interface{}]interface{}
}
@ -27,7 +27,7 @@ type AnyAnyMap struct {
// which is false in default.
func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[interface{}]interface{}),
}
}
@ -37,12 +37,12 @@ func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewAnyAnyMapFrom(data map[interface{}]interface{}, safe ...bool) *AnyAnyMap {
return &AnyAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// Iterator iterates the hash map readonly with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *AnyAnyMap) Iterator(f func(k interface{}, v interface{}) bool) {
m.mu.RLock()
@ -89,37 +89,44 @@ func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]interface{}, len(m.data))
for k, v := range m.data {
data[gconv.String(k)] = v
}
m.mu.RUnlock()
return data
}
// FilterEmpty deletes all key-value pair of which the value is empty.
func (m *AnyAnyMap) FilterEmpty() {
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range m.data {
if empty.IsEmpty(v) {
delete(m.data, k)
}
}
m.mu.Unlock()
}
// Set sets key-value to the hash map.
func (m *AnyAnyMap) Set(key interface{}, val interface{}) {
func (m *AnyAnyMap) Set(key interface{}, value interface{}) {
m.mu.Lock()
m.data[key] = val
if m.data == nil {
m.data = make(map[interface{}]interface{})
}
m.data[key] = value
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
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -128,17 +135,21 @@ func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
// Second return parameter <found> 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]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *AnyAnyMap) Get(key interface{}) interface{} {
func (m *AnyAnyMap) Get(key interface{}) (value interface{}) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -163,8 +174,10 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
if size == 0 {
return nil
}
index := 0
newMap := make(map[interface{}]interface{}, size)
var (
index = 0
newMap = make(map[interface{}]interface{}, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -188,6 +201,9 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
}
if v, ok := m.data[key]; ok {
return v
}
@ -235,26 +251,26 @@ func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) inte
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
// GetVar returns a Var with the value by given <key>.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVar(key interface{}) *gvar.Var {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSet returns a Var with result from GetVarOrSet.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
// The returned Var is un-concurrent safe.
func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
@ -293,21 +309,25 @@ func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{})
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *AnyAnyMap) Remove(key interface{}) interface{} {
func (m *AnyAnyMap) Remove(key interface{}) (value interface{}) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// 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)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
@ -315,36 +335,43 @@ func (m *AnyAnyMap) Removes(keys []interface{}) {
// Keys returns all keys of the map as a slice.
func (m *AnyAnyMap) Keys() []interface{} {
m.mu.RLock()
keys := make([]interface{}, len(m.data))
index := 0
defer m.mu.RUnlock()
var (
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
defer m.mu.RUnlock()
var (
values = make([]interface{}, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
}
m.mu.RUnlock()
return values
}
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *AnyAnyMap) Contains(key interface{}) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -405,6 +432,10 @@ func (m *AnyAnyMap) Flip() {
func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -421,12 +452,11 @@ func (m *AnyAnyMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[interface{}]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
}
var data map[string]interface{}
if err := json.Unmarshal(b, &data); err != nil {
return err
@ -439,12 +469,11 @@ func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *AnyAnyMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[interface{}]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]interface{})
}
for k, v := range gconv.Map(value) {
m.data[k] = v
}

View File

@ -18,7 +18,7 @@ import (
)
type IntAnyMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[int]interface{}
}
@ -27,7 +27,7 @@ type IntAnyMap struct {
// which is false in default.
func NewIntAnyMap(safe ...bool) *IntAnyMap {
return &IntAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[int]interface{}),
}
}
@ -37,12 +37,12 @@ func NewIntAnyMap(safe ...bool) *IntAnyMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewIntAnyMapFrom(data map[int]interface{}, safe ...bool) *IntAnyMap {
return &IntAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// Iterator iterates the hash map readonly with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *IntAnyMap) Iterator(f func(k int, v interface{}) bool) {
m.mu.RLock()
@ -111,6 +111,9 @@ func (m *IntAnyMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *IntAnyMap) Set(key int, val interface{}) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[int]interface{})
}
m.data[key] = val
m.mu.Unlock()
}
@ -118,8 +121,12 @@ func (m *IntAnyMap) Set(key int, val interface{}) {
// 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
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -128,17 +135,21 @@ func (m *IntAnyMap) Sets(data map[int]interface{}) {
// Second return parameter <found> 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]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *IntAnyMap) Get(key int) interface{} {
func (m *IntAnyMap) Get(key int) (value interface{}) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -163,8 +174,10 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
if size == 0 {
return nil
}
index := 0
newMap := make(map[int]interface{}, size)
var (
index = 0
newMap = make(map[int]interface{}, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -188,6 +201,9 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]interface{})
}
if v, ok := m.data[key]; ok {
return v
}
@ -233,26 +249,26 @@ func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{}
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
// GetVar returns a Var with the value by given <key>.
// The returned Var is un-concurrent safe.
func (m *IntAnyMap) GetVar(key int) *gvar.Var {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSet returns a Var with result from GetVarOrSet.
// The returned Var is un-concurrent safe.
func (m *IntAnyMap) GetVarOrSet(key int, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
// The returned Var is un-concurrent safe.
func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
// The returned Var is un-concurrent safe.
func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
@ -293,28 +309,34 @@ func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
// 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)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *IntAnyMap) Remove(key int) interface{} {
func (m *IntAnyMap) Remove(key int) (value interface{}) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// 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
var (
keys = make([]int, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -326,8 +348,10 @@ func (m *IntAnyMap) Keys() []int {
// 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
var (
values = make([]interface{}, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -339,10 +363,13 @@ func (m *IntAnyMap) Values() []interface{} {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *IntAnyMap) Contains(key int) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -403,6 +430,10 @@ func (m *IntAnyMap) Flip() {
func (m *IntAnyMap) Merge(other *IntAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -421,12 +452,11 @@ func (m *IntAnyMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]interface{})
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -435,12 +465,11 @@ func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]interface{})
}
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &m.data)

View File

@ -16,7 +16,7 @@ import (
)
type IntIntMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[int]int
}
@ -25,7 +25,7 @@ type IntIntMap struct {
// which is false in default.
func NewIntIntMap(safe ...bool) *IntIntMap {
return &IntIntMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[int]int),
}
}
@ -35,12 +35,12 @@ func NewIntIntMap(safe ...bool) *IntIntMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewIntIntMapFrom(data map[int]int, safe ...bool) *IntIntMap {
return &IntIntMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// Iterator iterates the hash map readonly with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *IntIntMap) Iterator(f func(k int, v int) bool) {
m.mu.RLock()
@ -109,6 +109,9 @@ func (m *IntIntMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *IntIntMap) Set(key int, val int) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[int]int)
}
m.data[key] = val
m.mu.Unlock()
}
@ -116,8 +119,12 @@ func (m *IntIntMap) Set(key int, val int) {
// 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
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -126,17 +133,21 @@ func (m *IntIntMap) Sets(data map[int]int) {
// Second return parameter <found> 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]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *IntIntMap) Get(key int) int {
func (m *IntIntMap) Get(key int) (value int) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -161,8 +172,10 @@ func (m *IntIntMap) Pops(size int) map[int]int {
if size == 0 {
return nil
}
index := 0
newMap := make(map[int]int, size)
var (
index = 0
newMap = make(map[int]int, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -181,12 +194,14 @@ func (m *IntIntMap) Pops(size int) map[int]int {
// It returns value with given <key>.
func (m *IntIntMap) doSetWithLockCheck(key int, value int) int {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]int)
}
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
}
m.data[key] = value
m.mu.Unlock()
return value
}
@ -219,6 +234,9 @@ 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 m.data == nil {
m.data = make(map[int]int)
}
if v, ok = m.data[key]; ok {
return v
}
@ -259,6 +277,9 @@ func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]int)
}
if _, ok := m.data[key]; !ok {
m.data[key] = f()
}
@ -270,28 +291,34 @@ func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
// Removes batch deletes values of the map by keys.
func (m *IntIntMap) Removes(keys []int) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *IntIntMap) Remove(key int) int {
func (m *IntIntMap) Remove(key int) (value int) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// 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
var (
keys = make([]int, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -303,8 +330,10 @@ func (m *IntIntMap) Keys() []int {
// 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
var (
values = make([]int, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -316,10 +345,13 @@ func (m *IntIntMap) Values() []int {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *IntIntMap) Contains(key int) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -380,6 +412,10 @@ func (m *IntIntMap) Flip() {
func (m *IntIntMap) Merge(other *IntIntMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -398,12 +434,11 @@ func (m *IntIntMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *IntIntMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]int)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]int)
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -412,12 +447,11 @@ func (m *IntIntMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]int)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]int)
}
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &m.data)

View File

@ -16,7 +16,7 @@ import (
)
type IntStrMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[int]string
}
@ -25,7 +25,7 @@ type IntStrMap struct {
// which is false in default.
func NewIntStrMap(safe ...bool) *IntStrMap {
return &IntStrMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[int]string),
}
}
@ -35,12 +35,12 @@ func NewIntStrMap(safe ...bool) *IntStrMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewIntStrMapFrom(data map[int]string, safe ...bool) *IntStrMap {
return &IntStrMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// Iterator iterates the hash map readonly with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *IntStrMap) Iterator(f func(k int, v string) bool) {
m.mu.RLock()
@ -109,6 +109,9 @@ func (m *IntStrMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *IntStrMap) Set(key int, val string) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[int]string)
}
m.data[key] = val
m.mu.Unlock()
}
@ -116,8 +119,12 @@ func (m *IntStrMap) Set(key int, val string) {
// 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
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -126,17 +133,21 @@ func (m *IntStrMap) Sets(data map[int]string) {
// Second return parameter <found> 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]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *IntStrMap) Get(key int) string {
func (m *IntStrMap) Get(key int) (value string) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -161,8 +172,10 @@ func (m *IntStrMap) Pops(size int) map[int]string {
if size == 0 {
return nil
}
index := 0
newMap := make(map[int]string, size)
var (
index = 0
newMap = make(map[int]string, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -182,6 +195,9 @@ func (m *IntStrMap) Pops(size int) map[int]string {
func (m *IntStrMap) doSetWithLockCheck(key int, value string) string {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]string)
}
if v, ok := m.data[key]; ok {
return v
}
@ -218,13 +234,14 @@ 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 m.data == nil {
m.data = make(map[int]string)
}
if v, ok = m.data[key]; ok {
return v
}
v = f()
if v != "" {
m.data[key] = v
}
m.data[key] = v
return v
} else {
return v
@ -260,6 +277,9 @@ func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]string)
}
if _, ok := m.data[key]; !ok {
m.data[key] = f()
}
@ -271,28 +291,34 @@ func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
// 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)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *IntStrMap) Remove(key int) string {
func (m *IntStrMap) Remove(key int) (value string) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// 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
var (
keys = make([]int, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -304,8 +330,10 @@ func (m *IntStrMap) Keys() []int {
// 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
var (
values = make([]string, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -317,10 +345,13 @@ func (m *IntStrMap) Values() []string {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *IntStrMap) Contains(key int) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -381,6 +412,10 @@ func (m *IntStrMap) Flip() {
func (m *IntStrMap) Merge(other *IntStrMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -399,12 +434,11 @@ func (m *IntStrMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *IntStrMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]string)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]string)
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -413,12 +447,11 @@ func (m *IntStrMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[int]string)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[int]string)
}
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &m.data)

View File

@ -18,7 +18,7 @@ import (
)
type StrAnyMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[string]interface{}
}
@ -27,7 +27,7 @@ type StrAnyMap struct {
// which is false in default.
func NewStrAnyMap(safe ...bool) *StrAnyMap {
return &StrAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[string]interface{}),
}
}
@ -37,12 +37,12 @@ func NewStrAnyMap(safe ...bool) *StrAnyMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewStrAnyMapFrom(data map[string]interface{}, safe ...bool) *StrAnyMap {
return &StrAnyMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// Iterator iterates the hash map readonly with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *StrAnyMap) Iterator(f func(k string, v interface{}) bool) {
m.mu.RLock()
@ -105,6 +105,9 @@ func (m *StrAnyMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *StrAnyMap) Set(key string, val interface{}) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[string]interface{})
}
m.data[key] = val
m.mu.Unlock()
}
@ -112,8 +115,12 @@ func (m *StrAnyMap) Set(key string, val interface{}) {
// 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
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -122,17 +129,21 @@ func (m *StrAnyMap) Sets(data map[string]interface{}) {
// Second return parameter <found> 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]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *StrAnyMap) Get(key string) interface{} {
func (m *StrAnyMap) Get(key string) (value interface{}) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -157,8 +168,10 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
if size == 0 {
return nil
}
index := 0
newMap := make(map[string]interface{}, size)
var (
index = 0
newMap = make(map[string]interface{}, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -182,6 +195,9 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]interface{})
}
if v, ok := m.data[key]; ok {
return v
}
@ -229,26 +245,26 @@ func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
// GetVar returns a Var with the value by given <key>.
// The returned Var is un-concurrent safe.
func (m *StrAnyMap) GetVar(key string) *gvar.Var {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSet returns a Var with result from GetVarOrSet.
// The returned Var is un-concurrent safe.
func (m *StrAnyMap) GetVarOrSet(key string, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
// The returned Var is un-concurrent safe.
func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
// The returned Var is un-concurrent safe.
func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
@ -289,28 +305,34 @@ func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool
// 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)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *StrAnyMap) Remove(key string) interface{} {
func (m *StrAnyMap) Remove(key string) (value interface{}) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// 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
var (
keys = make([]string, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -322,8 +344,10 @@ func (m *StrAnyMap) Keys() []string {
// 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
var (
values = make([]interface{}, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -335,10 +359,13 @@ func (m *StrAnyMap) Values() []interface{} {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *StrAnyMap) Contains(key string) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -399,6 +426,10 @@ func (m *StrAnyMap) Flip() {
func (m *StrAnyMap) Merge(other *StrAnyMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -417,12 +448,11 @@ func (m *StrAnyMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]interface{})
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -431,10 +461,6 @@ func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]interface{})
}
m.mu.Lock()
defer m.mu.Unlock()
m.data = gconv.Map(value)

View File

@ -16,7 +16,7 @@ import (
)
type StrIntMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[string]int
}
@ -25,7 +25,7 @@ type StrIntMap struct {
// which is false in default.
func NewStrIntMap(safe ...bool) *StrIntMap {
return &StrIntMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[string]int),
}
}
@ -35,12 +35,12 @@ func NewStrIntMap(safe ...bool) *StrIntMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewStrIntMapFrom(data map[string]int, safe ...bool) *StrIntMap {
return &StrIntMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// Iterator iterates the hash map readonly with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *StrIntMap) Iterator(f func(k string, v int) bool) {
m.mu.RLock()
@ -76,11 +76,11 @@ func (m *StrIntMap) Map() map[string]int {
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *StrIntMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]interface{}, len(m.data))
for k, v := range m.data {
data[k] = v
}
m.mu.RUnlock()
return data
}
@ -109,6 +109,9 @@ func (m *StrIntMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *StrIntMap) Set(key string, val int) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[string]int)
}
m.data[key] = val
m.mu.Unlock()
}
@ -116,8 +119,12 @@ func (m *StrIntMap) Set(key string, val int) {
// 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
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -126,17 +133,21 @@ func (m *StrIntMap) Sets(data map[string]int) {
// Second return parameter <found> 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]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *StrIntMap) Get(key string) int {
func (m *StrIntMap) Get(key string) (value int) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -161,8 +172,10 @@ func (m *StrIntMap) Pops(size int) map[string]int {
if size == 0 {
return nil
}
index := 0
newMap := make(map[string]int, size)
var (
index = 0
newMap = make(map[string]int, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -181,6 +194,9 @@ func (m *StrIntMap) Pops(size int) map[string]int {
// It returns value with given <key>.
func (m *StrIntMap) doSetWithLockCheck(key string, value int) int {
m.mu.Lock()
if m.data == nil {
m.data = make(map[string]int)
}
if v, ok := m.data[key]; ok {
m.mu.Unlock()
return v
@ -221,6 +237,9 @@ 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 m.data == nil {
m.data = make(map[string]int)
}
if v, ok = m.data[key]; ok {
return v
}
@ -261,6 +280,9 @@ func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]int)
}
if _, ok := m.data[key]; !ok {
m.data[key] = f()
}
@ -272,28 +294,34 @@ func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
// 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)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *StrIntMap) Remove(key string) int {
func (m *StrIntMap) Remove(key string) (value int) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// 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
var (
keys = make([]string, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -305,8 +333,10 @@ func (m *StrIntMap) Keys() []string {
// 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
var (
values = make([]int, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -318,10 +348,13 @@ func (m *StrIntMap) Values() []int {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *StrIntMap) Contains(key string) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -382,6 +415,10 @@ func (m *StrIntMap) Flip() {
func (m *StrIntMap) Merge(other *StrIntMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -400,12 +437,11 @@ func (m *StrIntMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *StrIntMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]int)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]int)
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -414,12 +450,11 @@ func (m *StrIntMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]int)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]int)
}
switch value.(type) {
case string, []byte:
return json.Unmarshal(gconv.Bytes(value), &m.data)

View File

@ -17,7 +17,7 @@ import (
)
type StrStrMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[string]string
}
@ -27,7 +27,7 @@ type StrStrMap struct {
func NewStrStrMap(safe ...bool) *StrStrMap {
return &StrStrMap{
data: make(map[string]string),
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
}
}
@ -36,12 +36,12 @@ func NewStrStrMap(safe ...bool) *StrStrMap {
// there might be some concurrent-safe issues when changing the map outside.
func NewStrStrMapFrom(data map[string]string, safe ...bool) *StrStrMap {
return &StrStrMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: data,
}
}
// Iterator iterates the hash map with custom callback function <f>.
// Iterator iterates the hash map readonly with custom callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *StrStrMap) Iterator(f func(k string, v string) bool) {
m.mu.RLock()
@ -110,6 +110,9 @@ func (m *StrStrMap) FilterEmpty() {
// Set sets key-value to the hash map.
func (m *StrStrMap) Set(key string, val string) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[string]string)
}
m.data[key] = val
m.mu.Unlock()
}
@ -117,8 +120,12 @@ func (m *StrStrMap) Set(key string, val string) {
// Sets batch sets key-values to the hash map.
func (m *StrStrMap) Sets(data map[string]string) {
m.mu.Lock()
for k, v := range data {
m.data[k] = v
if m.data == nil {
m.data = data
} else {
for k, v := range data {
m.data[k] = v
}
}
m.mu.Unlock()
}
@ -127,17 +134,21 @@ func (m *StrStrMap) Sets(data map[string]string) {
// Second return parameter <found> 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]
if m.data != nil {
value, found = m.data[key]
}
m.mu.RUnlock()
return
}
// Get returns the value by given <key>.
func (m *StrStrMap) Get(key string) string {
func (m *StrStrMap) Get(key string) (value string) {
m.mu.RLock()
val, _ := m.data[key]
if m.data != nil {
value, _ = m.data[key]
}
m.mu.RUnlock()
return val
return
}
// Pop retrieves and deletes an item from the map.
@ -162,8 +173,10 @@ func (m *StrStrMap) Pops(size int) map[string]string {
if size == 0 {
return nil
}
index := 0
newMap := make(map[string]string, size)
var (
index = 0
newMap = make(map[string]string, size)
)
for k, v := range m.data {
delete(m.data, k)
newMap[k] = v
@ -183,6 +196,9 @@ func (m *StrStrMap) Pops(size int) map[string]string {
func (m *StrStrMap) doSetWithLockCheck(key string, value string) string {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]string)
}
if v, ok := m.data[key]; ok {
return v
}
@ -221,13 +237,14 @@ 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 m.data == nil {
m.data = make(map[string]string)
}
if v, ok = m.data[key]; ok {
return v
}
v = f()
if v != "" {
m.data[key] = v
}
m.data[key] = v
return v
} else {
return v
@ -263,6 +280,9 @@ func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
if !m.Contains(key) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]string)
}
if _, ok := m.data[key]; !ok {
m.data[key] = f()
}
@ -274,28 +294,34 @@ func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
// Removes batch deletes values of the map by keys.
func (m *StrStrMap) Removes(keys []string) {
m.mu.Lock()
for _, key := range keys {
delete(m.data, key)
if m.data != nil {
for _, key := range keys {
delete(m.data, key)
}
}
m.mu.Unlock()
}
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *StrStrMap) Remove(key string) string {
func (m *StrStrMap) Remove(key string) (value string) {
m.mu.Lock()
val, exists := m.data[key]
if exists {
delete(m.data, key)
if m.data != nil {
var ok bool
if value, ok = m.data[key]; ok {
delete(m.data, key)
}
}
m.mu.Unlock()
return val
return
}
// 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
var (
keys = make([]string, len(m.data))
index = 0
)
for key := range m.data {
keys[index] = key
index++
@ -307,8 +333,10 @@ func (m *StrStrMap) Keys() []string {
// 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
var (
values = make([]string, len(m.data))
index = 0
)
for _, value := range m.data {
values[index] = value
index++
@ -320,10 +348,13 @@ func (m *StrStrMap) Values() []string {
// Contains checks whether a key exists.
// It returns true if the <key> exists, or else false.
func (m *StrStrMap) Contains(key string) bool {
var ok bool
m.mu.RLock()
_, exists := m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return exists
return ok
}
// Size returns the size of the map.
@ -384,6 +415,10 @@ func (m *StrStrMap) Flip() {
func (m *StrStrMap) Merge(other *StrStrMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = other.MapCopy()
return
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -402,12 +437,11 @@ func (m *StrStrMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *StrStrMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.data = make(map[string]string)
}
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[string]string)
}
if err := json.Unmarshal(b, &m.data); err != nil {
return err
}
@ -416,9 +450,6 @@ func (m *StrStrMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
}
m.mu.Lock()
defer m.mu.Unlock()
m.data = gconv.MapStrStr(value)

View File

@ -19,7 +19,7 @@ import (
)
type ListMap struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[interface{}]*glist.Element
list *glist.List
}
@ -35,7 +35,7 @@ type gListMapNode struct {
// which is false in default.
func NewListMap(safe ...bool) *ListMap {
return &ListMap{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[interface{}]*glist.Element),
list: glist.New(),
}
@ -55,28 +55,32 @@ func (m *ListMap) Iterator(f func(key, value interface{}) bool) {
m.IteratorAsc(f)
}
// IteratorAsc iterates the map in ascending order with given callback function <f>.
// IteratorAsc iterates the map readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *ListMap) IteratorAsc(f func(key interface{}, value interface{}) bool) {
m.mu.RLock()
defer m.mu.RUnlock()
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
if m.list != nil {
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
return f(node.key, node.value)
})
}
}
// IteratorDesc iterates the map in descending order with given callback function <f>.
// IteratorDesc iterates the map readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (m *ListMap) IteratorDesc(f func(key interface{}, value interface{}) bool) {
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)
})
if m.list != nil {
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.
@ -110,13 +114,16 @@ func (m *ListMap) Replace(data map[interface{}]interface{}) {
// Map returns a copy of the underlying 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
})
var node *gListMapNode
var data map[interface{}]interface{}
if m.list != 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
}
@ -124,13 +131,16 @@ func (m *ListMap) Map() map[interface{}]interface{} {
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
func (m *ListMap) MapStrAny() map[string]interface{} {
m.mu.RLock()
node := (*gListMapNode)(nil)
data := make(map[string]interface{}, len(m.data))
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
data[gconv.String(node.key)] = node.value
return true
})
var node *gListMapNode
var data map[string]interface{}
if m.list != nil {
data = make(map[string]interface{}, len(m.data))
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
data[gconv.String(node.key)] = node.value
return true
})
}
m.mu.RUnlock()
return data
}
@ -138,20 +148,22 @@ func (m *ListMap) MapStrAny() map[string]interface{} {
// FilterEmpty deletes all key-value pair of which the value is empty.
func (m *ListMap) FilterEmpty() {
m.mu.Lock()
keys := make([]interface{}, 0)
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
if empty.IsEmpty(node.value) {
keys = append(keys, node.key)
}
return true
})
if len(keys) > 0 {
for _, key := range keys {
if e, ok := m.data[key]; ok {
delete(m.data, key)
m.list.Remove(e)
if m.list != nil {
keys := make([]interface{}, 0)
node := (*gListMapNode)(nil)
m.list.IteratorAsc(func(e *glist.Element) bool {
node = e.Value.(*gListMapNode)
if empty.IsEmpty(node.value) {
keys = append(keys, node.key)
}
return true
})
if len(keys) > 0 {
for _, key := range keys {
if e, ok := m.data[key]; ok {
delete(m.data, key)
m.list.Remove(e)
}
}
}
}
@ -161,6 +173,10 @@ func (m *ListMap) FilterEmpty() {
// Set sets key-value to the map.
func (m *ListMap) Set(key interface{}, value interface{}) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
} else {
@ -172,6 +188,10 @@ func (m *ListMap) Set(key interface{}, value interface{}) {
// Sets batch sets key-values to the map.
func (m *ListMap) Sets(data map[interface{}]interface{}) {
m.mu.Lock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
for key, value := range data {
if e, ok := m.data[key]; !ok {
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
@ -186,9 +206,11 @@ func (m *ListMap) Sets(data map[interface{}]interface{}) {
// Second return parameter <found> 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
if m.data != nil {
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
found = ok
}
}
m.mu.RUnlock()
return
@ -197,8 +219,10 @@ func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
// Get returns the value by given <key>.
func (m *ListMap) Get(key interface{}) (value interface{}) {
m.mu.RLock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
if m.data != nil {
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
}
}
m.mu.RUnlock()
return
@ -255,6 +279,10 @@ func (m *ListMap) Pops(size int) map[interface{}]interface{} {
func (m *ListMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
if e, ok := m.data[key]; ok {
return e.Value.(*gListMapNode).value
}
@ -302,26 +330,26 @@ func (m *ListMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interf
}
}
// GetVar returns a gvar.Var with the value by given <key>.
// The returned gvar.Var is un-concurrent safe.
// GetVar returns a Var with the value by given <key>.
// The returned Var is un-concurrent safe.
func (m *ListMap) GetVar(key interface{}) *gvar.Var {
return gvar.New(m.Get(key))
}
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSet returns a Var with result from GetVarOrSet.
// The returned Var is un-concurrent safe.
func (m *ListMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
return gvar.New(m.GetOrSet(key, value))
}
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
// The returned Var is un-concurrent safe.
func (m *ListMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFunc(key, f))
}
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
// The returned gvar.Var is un-concurrent safe.
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
// The returned Var is un-concurrent safe.
func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
return gvar.New(m.GetOrSetFuncLock(key, f))
}
@ -362,10 +390,12 @@ func (m *ListMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) b
// Remove deletes value from map by given <key>, and return this deleted value.
func (m *ListMap) Remove(key interface{}) (value interface{}) {
m.mu.Lock()
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
delete(m.data, key)
m.list.Remove(e)
if m.data != nil {
if e, ok := m.data[key]; ok {
value = e.Value.(*gListMapNode).value
delete(m.data, key)
m.list.Remove(e)
}
}
m.mu.Unlock()
return
@ -374,10 +404,12 @@ func (m *ListMap) Remove(key interface{}) (value interface{}) {
// 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)
if m.data != nil {
for _, key := range keys {
if e, ok := m.data[key]; ok {
delete(m.data, key)
m.list.Remove(e)
}
}
}
m.mu.Unlock()
@ -386,13 +418,17 @@ func (m *ListMap) Removes(keys []interface{}) {
// Keys returns all keys of the map as a slice in ascending order.
func (m *ListMap) Keys() []interface{} {
m.mu.RLock()
keys := make([]interface{}, m.list.Len())
index := 0
m.list.IteratorAsc(func(e *glist.Element) bool {
keys[index] = e.Value.(*gListMapNode).key
index++
return true
})
var (
keys = make([]interface{}, m.list.Len())
index = 0
)
if m.list != nil {
m.list.IteratorAsc(func(e *glist.Element) bool {
keys[index] = e.Value.(*gListMapNode).key
index++
return true
})
}
m.mu.RUnlock()
return keys
}
@ -400,13 +436,17 @@ func (m *ListMap) Keys() []interface{} {
// 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
})
var (
values = make([]interface{}, m.list.Len())
index = 0
)
if m.list != nil {
m.list.IteratorAsc(func(e *glist.Element) bool {
values[index] = e.Value.(*gListMapNode).value
index++
return true
})
}
m.mu.RUnlock()
return values
}
@ -415,7 +455,9 @@ func (m *ListMap) Values() []interface{} {
// It returns true if the <key> exists, or else false.
func (m *ListMap) Contains(key interface{}) (ok bool) {
m.mu.RLock()
_, ok = m.data[key]
if m.data != nil {
_, ok = m.data[key]
}
m.mu.RUnlock()
return
}
@ -448,6 +490,10 @@ func (m *ListMap) Flip() {
func (m *ListMap) Merge(other *ListMap) {
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
if other != m {
other.mu.RLock()
defer other.mu.RUnlock()
@ -471,13 +517,12 @@ func (m *ListMap) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (m *ListMap) UnmarshalJSON(b []byte) error {
if m.mu == nil {
m.mu = rwmutex.New()
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
m.mu.Lock()
defer m.mu.Unlock()
var data map[string]interface{}
if err := json.Unmarshal(b, &data); err != nil {
return err
@ -494,13 +539,12 @@ func (m *ListMap) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (m *ListMap) UnmarshalValue(value interface{}) (err error) {
if m.mu == nil {
m.mu = rwmutex.New()
m.mu.Lock()
defer m.mu.Unlock()
if m.data == nil {
m.data = make(map[interface{}]*glist.Element)
m.list = glist.New()
}
m.mu.Lock()
defer m.mu.Unlock()
for k, v := range gconv.Map(value) {
if e, ok := m.data[k]; !ok {
m.data[k] = m.list.PushBack(&gListMapNode{k, v})

View File

@ -7,6 +7,7 @@
package gmap_test
import (
"github.com/gogf/gf/util/gutil"
"testing"
"github.com/gogf/gf/container/gmap"
@ -17,6 +18,55 @@ func getValue() interface{} {
return 3
}
func Test_Map_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.Map
m.Set(1, 11)
t.Assert(m.Get(1), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.IntAnyMap
m.Set(1, 11)
t.Assert(m.Get(1), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.IntIntMap
m.Set(1, 11)
t.Assert(m.Get(1), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.IntStrMap
m.Set(1, "11")
t.Assert(m.Get(1), "11")
})
gtest.C(t, func(t *gtest.T) {
var m gmap.StrAnyMap
m.Set("1", "11")
t.Assert(m.Get("1"), "11")
})
gtest.C(t, func(t *gtest.T) {
var m gmap.StrStrMap
m.Set("1", "11")
t.Assert(m.Get("1"), "11")
})
gtest.C(t, func(t *gtest.T) {
var m gmap.StrIntMap
m.Set("1", 11)
t.Assert(m.Get("1"), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.ListMap
m.Set("1", 11)
t.Assert(m.Get("1"), 11)
})
gtest.C(t, func(t *gtest.T) {
var m gmap.TreeMap
m.SetComparator(gutil.ComparatorString)
m.Set("1", 11)
t.Assert(m.Get("1"), 11)
})
}
func Test_Map_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.New()

View File

@ -17,8 +17,34 @@ import (
"github.com/gogf/gf/test/gtest"
)
func anyAnyCallBack(int, interface{}) bool {
return true
func Test_AnyAnyMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.AnyAnyMap
m.Set(1, 1)
t.Assert(m.Get(1), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet(2, "2"), "2")
t.Assert(m.SetIfNotExist(2, "2"), false)
t.Assert(m.SetIfNotExist(3, 3), true)
t.Assert(m.Remove(2), "2")
t.Assert(m.Contains(2), false)
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_AnyAnyMap_Basic(t *testing.T) {

View File

@ -20,9 +20,37 @@ import (
func getAny() interface{} {
return 123
}
func intAnyCallBack(int, interface{}) bool {
return true
func Test_IntAnyMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.IntAnyMap
m.Set(1, 1)
t.Assert(m.Get(1), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet(2, "2"), "2")
t.Assert(m.SetIfNotExist(2, "2"), false)
t.Assert(m.SetIfNotExist(3, 3), true)
t.Assert(m.Remove(2), "2")
t.Assert(m.Contains(2), false)
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_IntAnyMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()
@ -55,6 +83,7 @@ func Test_IntAnyMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
})
}
func Test_IntAnyMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntAnyMap()

View File

@ -20,9 +20,41 @@ import (
func getInt() int {
return 123
}
func intIntCallBack(int, int) bool {
return true
}
func Test_IntIntMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.IntIntMap
m.Set(1, 1)
t.Assert(m.Get(1), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet(2, 2), 2)
t.Assert(m.SetIfNotExist(2, 2), false)
t.Assert(m.SetIfNotExist(3, 3), true)
t.Assert(m.Remove(2), 2)
t.Assert(m.Contains(2), false)
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
t.Assert(m.Map(), map[int]int{1: 1, 3: 3})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_IntIntMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()
@ -55,6 +87,7 @@ func Test_IntIntMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
})
}
func Test_IntIntMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntIntMap()

View File

@ -20,9 +20,40 @@ import (
func getStr() string {
return "z"
}
func intStrCallBack(int, string) bool {
return true
func Test_IntStrMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.IntStrMap
m.Set(1, "a")
t.Assert(m.Get(1), "a")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet(2, "b"), "b")
t.Assert(m.SetIfNotExist(2, "b"), false)
t.Assert(m.SetIfNotExist(3, "c"), true)
t.Assert(m.Remove(2), "b")
t.Assert(m.Contains(2), false)
t.AssertIN(3, m.Keys())
t.AssertIN(1, m.Keys())
t.AssertIN("a", m.Values())
t.AssertIN("c", m.Values())
m_f := gmap.NewIntStrMap()
m_f.Set(1, "2")
m_f.Flip()
t.Assert(m_f.Map(), map[int]string{2: "1"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_IntStrMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()
@ -60,6 +91,7 @@ func Test_IntStrMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
})
}
func Test_IntStrMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewIntStrMap()

View File

@ -17,6 +17,38 @@ import (
"github.com/gogf/gf/test/gtest"
)
func Test_ListMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.ListMap
m.Set("key1", "val1")
t.Assert(m.Keys(), []interface{}{"key1"})
t.Assert(m.Get("key1"), "val1")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("key2", "val2"), "val2")
t.Assert(m.SetIfNotExist("key2", "val2"), false)
t.Assert(m.SetIfNotExist("key3", "val3"), true)
t.Assert(m.Remove("key2"), "val2")
t.Assert(m.Contains("key2"), false)
t.AssertIN("key3", m.Keys())
t.AssertIN("key1", m.Keys())
t.AssertIN("val3", m.Values())
t.AssertIN("val1", m.Values())
m.Flip()
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_ListMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()
@ -51,6 +83,7 @@ func Test_ListMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_ListMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewListMap()

View File

@ -17,9 +17,37 @@ import (
"github.com/gogf/gf/test/gtest"
)
func stringAnyCallBack(string, interface{}) bool {
return true
func Test_StrAnyMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.StrAnyMap
m.Set("a", 1)
t.Assert(m.Get("a"), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("b", "2"), "2")
t.Assert(m.SetIfNotExist("b", "2"), false)
t.Assert(m.SetIfNotExist("c", 3), true)
t.Assert(m.Remove("b"), "2")
t.Assert(m.Contains("b"), false)
t.AssertIN("c", m.Keys())
t.AssertIN("a", m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m.Flip()
t.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_StrAnyMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrAnyMap()
@ -53,6 +81,7 @@ func Test_StrAnyMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"})
})
}
func Test_StrAnyMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrAnyMap()

View File

@ -17,9 +17,39 @@ import (
"github.com/gogf/gf/test/gtest"
)
func stringIntCallBack(string, int) bool {
return true
func Test_StrIntMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.StrIntMap
m.Set("a", 1)
t.Assert(m.Get("a"), 1)
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("b", 2), 2)
t.Assert(m.SetIfNotExist("b", 2), false)
t.Assert(m.SetIfNotExist("c", 3), true)
t.Assert(m.Remove("b"), 2)
t.Assert(m.Contains("b"), false)
t.AssertIN("c", m.Keys())
t.AssertIN("a", m.Keys())
t.AssertIN(3, m.Values())
t.AssertIN(1, m.Values())
m_f := gmap.NewStrIntMap()
m_f.Set("1", 2)
m_f.Flip()
t.Assert(m_f.Map(), map[string]int{"2": 1})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_StrIntMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrIntMap()
@ -55,6 +85,7 @@ func Test_StrIntMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[string]int{"a": 1, "b": 2})
})
}
func Test_StrIntMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrIntMap()

View File

@ -17,9 +17,38 @@ import (
"github.com/gogf/gf/test/gtest"
)
func stringStrCallBack(string, string) bool {
return true
func Test_StrStrMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.StrStrMap
m.Set("a", "a")
t.Assert(m.Get("a"), "a")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("b", "b"), "b")
t.Assert(m.SetIfNotExist("b", "b"), false)
t.Assert(m.SetIfNotExist("c", "c"), true)
t.Assert(m.Remove("b"), "b")
t.Assert(m.Contains("b"), false)
t.AssertIN("c", m.Keys())
t.AssertIN("a", m.Keys())
t.AssertIN("a", m.Values())
t.AssertIN("c", m.Values())
m.Flip()
t.Assert(m.Map(), map[string]string{"a": "a", "c": "c"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_StrStrMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrStrMap()
@ -54,6 +83,7 @@ func Test_StrStrMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[string]string{"a": "a", "b": "b"})
})
}
func Test_StrStrMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewStrStrMap()

View File

@ -17,6 +17,39 @@ import (
"github.com/gogf/gf/util/gutil"
)
func Test_TreeMap_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var m gmap.TreeMap
m.SetComparator(gutil.ComparatorString)
m.Set("key1", "val1")
t.Assert(m.Keys(), []interface{}{"key1"})
t.Assert(m.Get("key1"), "val1")
t.Assert(m.Size(), 1)
t.Assert(m.IsEmpty(), false)
t.Assert(m.GetOrSet("key2", "val2"), "val2")
t.Assert(m.SetIfNotExist("key2", "val2"), false)
t.Assert(m.SetIfNotExist("key3", "val3"), true)
t.Assert(m.Remove("key2"), "val2")
t.Assert(m.Contains("key2"), false)
t.AssertIN("key3", m.Keys())
t.AssertIN("key1", m.Keys())
t.AssertIN("val3", m.Values())
t.AssertIN("val1", m.Values())
m.Flip()
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
m.Clear()
t.Assert(m.Size(), 0)
t.Assert(m.IsEmpty(), true)
})
}
func Test_TreeMap_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewTreeMap(gutil.ComparatorString)
@ -51,6 +84,7 @@ func Test_TreeMap_Basic(t *testing.T) {
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
})
}
func Test_TreeMap_Set_Fun(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := gmap.NewTreeMap(gutil.ComparatorString)

View File

@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/internal/rwmutex"
)
// Ring is a struct of ring structure.
type Ring struct {
mu *rwmutex.RWMutex
ring *ring.Ring // Underlying ring.
@ -22,6 +23,9 @@ type Ring struct {
dirty *gtype.Bool // Dirty, which means the len and cap should be recalculated. It's marked dirty when the size of ring changes.
}
// New creates and returns a Ring structure of <cap> elements.
// The optional parameter <safe> specifies whether using this structure in concurrent safety,
// which is false in default.
func New(cap int, safe ...bool) *Ring {
return &Ring{
mu: rwmutex.New(safe...),

View File

@ -1,3 +1,9 @@
// 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 gring_test
import (

View File

@ -16,7 +16,7 @@ import (
)
type Set struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[interface{}]struct{}
}
@ -31,7 +31,7 @@ func New(safe ...bool) *Set {
func NewSet(safe ...bool) *Set {
return &Set{
data: make(map[interface{}]struct{}),
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
}
}
@ -44,13 +44,13 @@ func NewFrom(items interface{}, safe ...bool) *Set {
}
return &Set{
data: m,
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
}
}
// Iterator iterates the set with given callback function <f>,
// Iterator iterates the set readonly with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
func (set *Set) Iterator(f func(v interface{}) bool) *Set {
func (set *Set) Iterator(f func(v interface{}) bool) {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.data {
@ -58,77 +58,113 @@ func (set *Set) Iterator(f func(v interface{}) bool) *Set {
break
}
}
return set
}
// Add adds one or multiple items to the set.
func (set *Set) Add(item ...interface{}) *Set {
func (set *Set) Add(item ...interface{}) {
set.mu.Lock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
for _, v := range item {
set.data[v] = struct{}{}
}
set.mu.Unlock()
return set
}
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
func (set *Set) AddIfNotExistFunc(item interface{}, f func() interface{}) *Set {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f())
// AddIfNotExist checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set,
// or else it does nothing and returns false.
//
// Note that, if <item> is nil, it does nothing and returns false.
func (set *Set) AddIfNotExist(item interface{}) bool {
if item == nil {
return false
}
return set
}
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
//
// Note that the callback function <f> is executed in the mutex.Lock of the set.
func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() interface{}) *Set {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f)
}
return set
}
// doAddWithLockCheck checks whether item exists with mutex.Lock,
// if not exists, it adds item to the set or else just returns the existing value.
//
// If <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the set,
// and its return value will be added to the set.
//
// It returns item successfully added..
func (set *Set) doAddWithLockCheck(item interface{}, value interface{}) interface{} {
set.mu.Lock()
defer set.mu.Unlock()
if _, ok := set.data[item]; !ok && value != nil {
if f, ok := value.(func() interface{}); ok {
item = f()
} else {
item = value
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
if item != nil {
set.data[item] = struct{}{}
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, if <item> is nil, it does nothing and returns false. The function <f>
// is executed without writing lock.
func (set *Set) AddIfNotExistFunc(item interface{}, f func() bool) bool {
if item == nil {
return false
}
return item
if !set.Contains(item) {
if f() {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, if <item> is nil, it does nothing and returns false. The function <f>
// is executed within writing lock.
func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() bool) bool {
if item == nil {
return false
}
if !set.Contains(item) {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
if f() {
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// Contains checks whether the set contains <item>.
func (set *Set) Contains(item interface{}) bool {
var ok bool
set.mu.RLock()
_, exists := set.data[item]
if set.data != nil {
_, ok = set.data[item]
}
set.mu.RUnlock()
return exists
return ok
}
// Remove deletes <item> from set.
func (set *Set) Remove(item interface{}) *Set {
func (set *Set) Remove(item interface{}) {
set.mu.Lock()
delete(set.data, item)
if set.data != nil {
delete(set.data, item)
}
set.mu.Unlock()
return set
}
// Size returns the size of the set.
@ -140,18 +176,19 @@ func (set *Set) Size() int {
}
// Clear deletes all items of the set.
func (set *Set) Clear() *Set {
func (set *Set) Clear() {
set.mu.Lock()
set.data = make(map[interface{}]struct{})
set.mu.Unlock()
return set
}
// Slice returns the a of items of the set as slice.
func (set *Set) Slice() []interface{} {
set.mu.RLock()
i := 0
ret := make([]interface{}, len(set.data))
var (
i = 0
ret = make([]interface{}, len(set.data))
)
for item := range set.data {
ret[i] = item
i++
@ -164,9 +201,14 @@ func (set *Set) Slice() []interface{} {
func (set *Set) Join(glue string) string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
l := len(set.data)
i := 0
if len(set.data) == 0 {
return ""
}
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
for k, _ := range set.data {
buffer.WriteString(gconv.String(k))
if i != l-1 {
@ -181,11 +223,13 @@ func (set *Set) Join(glue string) string {
func (set *Set) String() string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
var (
s = ""
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
buffer.WriteByte('[')
s := ""
l := len(set.data)
i := 0
for k, _ := range set.data {
s = gconv.String(k)
if gstr.IsNumeric(s) {
@ -256,7 +300,7 @@ func (set *Set) IsSubsetOf(other *Set) bool {
// Union returns a new set which is the union of <set> and <others>.
// Which means, all the items in <newSet> are in <set> or in <others>.
func (set *Set) Union(others ...*Set) (newSet *Set) {
newSet = NewSet(true)
newSet = NewSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -282,7 +326,7 @@ func (set *Set) Union(others ...*Set) (newSet *Set) {
// Diff returns a new set which is the difference set from <set> to <others>.
// Which means, all the items in <newSet> are in <set> but not in <others>.
func (set *Set) Diff(others ...*Set) (newSet *Set) {
newSet = NewSet(true)
newSet = NewSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -303,7 +347,7 @@ func (set *Set) Diff(others ...*Set) (newSet *Set) {
// Intersect returns a new set which is the intersection from <set> to <others>.
// Which means, all the items in <newSet> are in <set> and also in <others>.
func (set *Set) Intersect(others ...*Set) (newSet *Set) {
newSet = NewSet(true)
newSet = NewSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -328,7 +372,7 @@ func (set *Set) Intersect(others ...*Set) (newSet *Set) {
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *Set) Complement(full *Set) (newSet *Set) {
newSet = NewSet(true)
newSet = NewSet()
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
@ -415,12 +459,11 @@ func (set *Set) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (set *Set) UnmarshalJSON(b []byte) error {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[interface{}]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
var array []interface{}
if err := json.Unmarshal(b, &array); err != nil {
return err
@ -433,12 +476,11 @@ func (set *Set) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for set.
func (set *Set) UnmarshalValue(value interface{}) (err error) {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[interface{}]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[interface{}]struct{})
}
var array []interface{}
switch value.(type) {
case string, []byte:

View File

@ -15,7 +15,7 @@ import (
)
type IntSet struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[int]struct{}
}
@ -24,7 +24,7 @@ type IntSet struct {
// which is false in default.
func NewIntSet(safe ...bool) *IntSet {
return &IntSet{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[int]struct{}),
}
}
@ -36,14 +36,14 @@ func NewIntSetFrom(items []int, safe ...bool) *IntSet {
m[v] = struct{}{}
}
return &IntSet{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: m,
}
}
// Iterator iterates the set with given callback function <f>,
// Iterator iterates the set readonly with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
func (set *IntSet) Iterator(f func(v int) bool) *IntSet {
func (set *IntSet) Iterator(f func(v int) bool) {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.data {
@ -51,75 +51,102 @@ func (set *IntSet) Iterator(f func(v int) bool) *IntSet {
break
}
}
return set
}
// Add adds one or multiple items to the set.
func (set *IntSet) Add(item ...int) *IntSet {
func (set *IntSet) Add(item ...int) {
set.mu.Lock()
if set.data == nil {
set.data = make(map[int]struct{})
}
for _, v := range item {
set.data[v] = struct{}{}
}
set.mu.Unlock()
return set
}
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
func (set *IntSet) AddIfNotExistFunc(item int, f func() int) *IntSet {
// AddIfNotExist checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set,
// or else it does nothing and returns false.
//
// Note that, if <item> is nil, it does nothing and returns false.
func (set *IntSet) AddIfNotExist(item int) bool {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f())
}
return set
}
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
//
// Note that the callback function <f> is executed in the mutex.Lock of the set.
func (set *IntSet) AddIfNotExistFuncLock(item int, f func() int) *IntSet {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f)
}
return set
}
// doAddWithLockCheck checks whether item exists with mutex.Lock,
// if not exists, it adds item to the set or else just returns the existing value.
//
// If <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the set,
// and its return value will be added to the set.
//
// It returns item successfully added..
func (set *IntSet) doAddWithLockCheck(item int, value interface{}) int {
set.mu.Lock()
defer set.mu.Unlock()
if _, ok := set.data[item]; !ok && value != nil {
if f, ok := value.(func() int); ok {
item = f()
} else {
item = value.(int)
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
set.data[item] = struct{}{}
return item
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, the function <f> is executed without writing lock.
func (set *IntSet) AddIfNotExistFunc(item int, f func() bool) bool {
if !set.Contains(item) {
if f() {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, the function <f> is executed without writing lock.
func (set *IntSet) AddIfNotExistFuncLock(item int, f func() bool) bool {
if !set.Contains(item) {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
if f() {
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// Contains checks whether the set contains <item>.
func (set *IntSet) Contains(item int) bool {
var ok bool
set.mu.RLock()
_, exists := set.data[item]
if set.data != nil {
_, ok = set.data[item]
}
set.mu.RUnlock()
return exists
return ok
}
// Remove deletes <item> from set.
func (set *IntSet) Remove(item int) *IntSet {
func (set *IntSet) Remove(item int) {
set.mu.Lock()
delete(set.data, item)
if set.data != nil {
delete(set.data, item)
}
set.mu.Unlock()
return set
}
// Size returns the size of the set.
@ -131,18 +158,19 @@ func (set *IntSet) Size() int {
}
// Clear deletes all items of the set.
func (set *IntSet) Clear() *IntSet {
func (set *IntSet) Clear() {
set.mu.Lock()
set.data = make(map[int]struct{})
set.mu.Unlock()
return set
}
// Slice returns the a of items of the set as slice.
func (set *IntSet) Slice() []int {
set.mu.RLock()
ret := make([]int, len(set.data))
i := 0
var (
i = 0
ret = make([]int, len(set.data))
)
for k, _ := range set.data {
ret[i] = k
i++
@ -155,9 +183,14 @@ func (set *IntSet) Slice() []int {
func (set *IntSet) Join(glue string) string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
l := len(set.data)
i := 0
if len(set.data) == 0 {
return ""
}
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
for k, _ := range set.data {
buffer.WriteString(gconv.String(k))
if i != l-1 {
@ -227,7 +260,7 @@ func (set *IntSet) IsSubsetOf(other *IntSet) bool {
// Union returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> are in <set> or in <other>.
func (set *IntSet) Union(others ...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
newSet = NewIntSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -253,7 +286,7 @@ func (set *IntSet) Union(others ...*IntSet) (newSet *IntSet) {
// Diff returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> are in <set> but not in <other>.
func (set *IntSet) Diff(others ...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
newSet = NewIntSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -274,7 +307,7 @@ func (set *IntSet) Diff(others ...*IntSet) (newSet *IntSet) {
// Intersect returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> are in <set> and also in <other>.
func (set *IntSet) Intersect(others ...*IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
newSet = NewIntSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -299,7 +332,7 @@ func (set *IntSet) Intersect(others ...*IntSet) (newSet *IntSet) {
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
newSet = NewIntSet(true)
newSet = NewIntSet()
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
@ -386,12 +419,11 @@ func (set *IntSet) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (set *IntSet) UnmarshalJSON(b []byte) error {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[int]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
var array []int
if err := json.Unmarshal(b, &array); err != nil {
return err
@ -404,12 +436,11 @@ func (set *IntSet) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for set.
func (set *IntSet) UnmarshalValue(value interface{}) (err error) {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[int]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[int]struct{})
}
var array []int
switch value.(type) {
case string, []byte:

View File

@ -16,7 +16,7 @@ import (
)
type StrSet struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
data map[string]struct{}
}
@ -25,7 +25,7 @@ type StrSet struct {
// which is false in default.
func NewStrSet(safe ...bool) *StrSet {
return &StrSet{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: make(map[string]struct{}),
}
}
@ -37,14 +37,14 @@ func NewStrSetFrom(items []string, safe ...bool) *StrSet {
m[v] = struct{}{}
}
return &StrSet{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
data: m,
}
}
// Iterator iterates the set with given callback function <f>,
// Iterator iterates the set readonly with given callback function <f>,
// if <f> returns true then continue iterating; or false to stop.
func (set *StrSet) Iterator(f func(v string) bool) *StrSet {
func (set *StrSet) Iterator(f func(v string) bool) {
set.mu.RLock()
defer set.mu.RUnlock()
for k, _ := range set.data {
@ -52,77 +52,100 @@ func (set *StrSet) Iterator(f func(v string) bool) *StrSet {
break
}
}
return set
}
// Add adds one or multiple items to the set.
func (set *StrSet) Add(item ...string) *StrSet {
func (set *StrSet) Add(item ...string) {
set.mu.Lock()
if set.data == nil {
set.data = make(map[string]struct{})
}
for _, v := range item {
set.data[v] = struct{}{}
}
set.mu.Unlock()
return set
}
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
func (set *StrSet) AddIfNotExistFunc(item string, f func() string) *StrSet {
// AddIfNotExist checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set,
// or else it does nothing and returns false.
func (set *StrSet) AddIfNotExist(item string) bool {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f())
}
return set
}
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
// if <item> does not exit in the set.
//
// Note that the callback function <f> is executed in the mutex.Lock of the set.
func (set *StrSet) AddIfNotExistFuncLock(item string, f func() string) *StrSet {
if !set.Contains(item) {
set.doAddWithLockCheck(item, f)
}
return set
}
// doAddWithLockCheck checks whether item exists with mutex.Lock,
// if not exists, it adds item to the set or else just returns the existing value.
//
// If <value> is type of <func() interface {}>,
// it will be executed with mutex.Lock of the set,
// and its return value will be added to the set.
//
// It returns item successfully added..
func (set *StrSet) doAddWithLockCheck(item string, value interface{}) string {
set.mu.Lock()
defer set.mu.Unlock()
if _, ok := set.data[item]; !ok && value != nil {
if f, ok := value.(func() string); ok {
item = f()
} else {
item = value.(string)
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
if item != "" {
set.data[item] = struct{}{}
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, the function <f> is executed without writing lock.
func (set *StrSet) AddIfNotExistFunc(item string, f func() bool) bool {
if !set.Contains(item) {
if f() {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return item
return false
}
// AddIfNotExistFunc checks whether item exists in the set,
// it adds the item to set and returns true if it does not exists in the set and
// function <f> returns true, or else it does nothing and returns false.
//
// Note that, the function <f> is executed without writing lock.
func (set *StrSet) AddIfNotExistFuncLock(item string, f func() bool) bool {
if !set.Contains(item) {
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
if f() {
if _, ok := set.data[item]; !ok {
set.data[item] = struct{}{}
return true
}
}
}
return false
}
// Contains checks whether the set contains <item>.
func (set *StrSet) Contains(item string) bool {
var ok bool
set.mu.RLock()
_, exists := set.data[item]
if set.data != nil {
_, ok = set.data[item]
}
set.mu.RUnlock()
return exists
return ok
}
// Remove deletes <item> from set.
func (set *StrSet) Remove(item string) *StrSet {
func (set *StrSet) Remove(item string) {
set.mu.Lock()
delete(set.data, item)
if set.data != nil {
delete(set.data, item)
}
set.mu.Unlock()
return set
}
// Size returns the size of the set.
@ -134,18 +157,19 @@ func (set *StrSet) Size() int {
}
// Clear deletes all items of the set.
func (set *StrSet) Clear() *StrSet {
func (set *StrSet) Clear() {
set.mu.Lock()
set.data = make(map[string]struct{})
set.mu.Unlock()
return set
}
// Slice returns the a of items of the set as slice.
func (set *StrSet) Slice() []string {
set.mu.RLock()
ret := make([]string, len(set.data))
i := 0
var (
i = 0
ret = make([]string, len(set.data))
)
for item := range set.data {
ret[i] = item
i++
@ -159,9 +183,14 @@ func (set *StrSet) Slice() []string {
func (set *StrSet) Join(glue string) string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
l := len(set.data)
i := 0
if len(set.data) == 0 {
return ""
}
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
for k, _ := range set.data {
buffer.WriteString(k)
if i != l-1 {
@ -176,9 +205,11 @@ func (set *StrSet) Join(glue string) string {
func (set *StrSet) String() string {
set.mu.RLock()
defer set.mu.RUnlock()
buffer := bytes.NewBuffer(nil)
l := len(set.data)
i := 0
var (
l = len(set.data)
i = 0
buffer = bytes.NewBuffer(nil)
)
for k, _ := range set.data {
buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`)
if i != l-1 {
@ -243,7 +274,7 @@ func (set *StrSet) IsSubsetOf(other *StrSet) bool {
// Union returns a new set which is the union of <set> and <other>.
// Which means, all the items in <newSet> are in <set> or in <other>.
func (set *StrSet) Union(others ...*StrSet) (newSet *StrSet) {
newSet = NewStrSet(true)
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -269,7 +300,7 @@ func (set *StrSet) Union(others ...*StrSet) (newSet *StrSet) {
// Diff returns a new set which is the difference set from <set> to <other>.
// Which means, all the items in <newSet> are in <set> but not in <other>.
func (set *StrSet) Diff(others ...*StrSet) (newSet *StrSet) {
newSet = NewStrSet(true)
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -290,7 +321,7 @@ func (set *StrSet) Diff(others ...*StrSet) (newSet *StrSet) {
// Intersect returns a new set which is the intersection from <set> to <other>.
// Which means, all the items in <newSet> are in <set> and also in <other>.
func (set *StrSet) Intersect(others ...*StrSet) (newSet *StrSet) {
newSet = NewStrSet(true)
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
for _, other := range others {
@ -315,7 +346,7 @@ func (set *StrSet) Intersect(others ...*StrSet) (newSet *StrSet) {
// It returns the difference between <full> and <set>
// if the given set <full> is not the full set of <set>.
func (set *StrSet) Complement(full *StrSet) (newSet *StrSet) {
newSet = NewStrSet(true)
newSet = NewStrSet()
set.mu.RLock()
defer set.mu.RUnlock()
if set != full {
@ -402,12 +433,11 @@ func (set *StrSet) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (set *StrSet) UnmarshalJSON(b []byte) error {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[string]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
var array []string
if err := json.Unmarshal(b, &array); err != nil {
return err
@ -420,12 +450,11 @@ func (set *StrSet) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for set.
func (set *StrSet) UnmarshalValue(value interface{}) (err error) {
if set.mu == nil {
set.mu = rwmutex.New()
set.data = make(map[string]struct{})
}
set.mu.Lock()
defer set.mu.Unlock()
if set.data == nil {
set.data = make(map[string]struct{})
}
var array []string
switch value.(type) {
case string, []byte:

View File

@ -13,6 +13,8 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
"strings"
"sync"
"time"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gset"
@ -21,10 +23,30 @@ import (
"testing"
)
func TestSet_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var s gset.Set
s.Add(1, 1, 2)
s.Add([]interface{}{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
t.AssertIN(2, s.Slice())
t.AssertIN(3, s.Slice())
t.AssertIN(4, s.Slice())
t.AssertNI(0, s.Slice())
t.Assert(s.Contains(4), true)
t.Assert(s.Contains(5), false)
s.Remove(1)
t.Assert(s.Size(), 3)
s.Clear()
t.Assert(s.Size(), 0)
})
}
func TestSet_New(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New()
s.Add(1).Add(1).Add(2)
s.Add(1, 1, 2)
s.Add([]interface{}{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
@ -44,7 +66,7 @@ func TestSet_New(t *testing.T) {
func TestSet_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewSet()
s.Add(1).Add(1).Add(2)
s.Add(1, 1, 2)
s.Add([]interface{}{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
@ -64,7 +86,7 @@ func TestSet_Basic(t *testing.T) {
func TestSet_Iterator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewSet()
s.Add(1).Add(2).Add(3)
s.Add(1, 2, 3)
t.Assert(s.Size(), 3)
a1 := garray.New(true)
@ -85,7 +107,7 @@ func TestSet_Iterator(t *testing.T) {
func TestSet_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewSet()
s.Add(1).Add(2).Add(3)
s.Add(1, 2, 3)
t.Assert(s.Size(), 3)
s.LockFunc(func(m map[interface{}]struct{}) {
delete(m, 1)
@ -105,9 +127,9 @@ func TestSet_Equal(t *testing.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s3 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2, 3)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
t.Assert(s1.Equal(s2), true)
t.Assert(s1.Equal(s3), false)
})
@ -118,9 +140,9 @@ func TestSet_IsSubsetOf(t *testing.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s3 := gset.NewSet()
s1.Add(1).Add(2)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
t.Assert(s1.IsSubsetOf(s2), true)
t.Assert(s2.IsSubsetOf(s3), true)
t.Assert(s1.IsSubsetOf(s3), true)
@ -133,8 +155,8 @@ func TestSet_Union(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2)
s2.Add(3).Add(4)
s1.Add(1, 2)
s2.Add(3, 4)
s3 := s1.Union(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(2), true)
@ -147,8 +169,8 @@ func TestSet_Diff(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Diff(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(2), true)
@ -161,8 +183,8 @@ func TestSet_Intersect(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Intersect(s2)
t.Assert(s3.Contains(1), false)
t.Assert(s3.Contains(2), false)
@ -175,8 +197,8 @@ func TestSet_Complement(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Complement(s2)
t.Assert(s3.Contains(1), false)
t.Assert(s3.Contains(2), false)
@ -203,9 +225,9 @@ func TestNewFrom(t *testing.T) {
func TestNew(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New()
s1.Add("a").Add(2)
s1.Add("a", 2)
s2 := gset.New(true)
s2.Add("b").Add(3)
s2.Add("b", 3)
t.Assert(s1.Contains("a"), true)
})
@ -214,13 +236,13 @@ func TestNew(t *testing.T) {
func TestSet_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add("a").Add("a1").Add("b").Add("c")
s1.Add("a", "a1", "b", "c")
str1 := s1.Join(",")
t.Assert(strings.Contains(str1, "a1"), true)
})
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add("a").Add(`"b"`).Add(`\c`)
s1.Add("a", `"b"`, `\c`)
str1 := s1.Join(",")
t.Assert(strings.Contains(str1, `"b"`), true)
t.Assert(strings.Contains(str1, `\c`), true)
@ -231,7 +253,7 @@ func TestSet_Join(t *testing.T) {
func TestSet_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add("a").Add("a2").Add("b").Add("c")
s1.Add("a", "a2", "b", "c")
str1 := s1.String()
t.Assert(strings.Contains(str1, "["), true)
t.Assert(strings.Contains(str1, "]"), true)
@ -243,8 +265,8 @@ func TestSet_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s2 := gset.New(true)
s1.Add("a").Add("a2").Add("b").Add("c")
s2.Add("b").Add("b1").Add("e").Add("f")
s1.Add("a", "a2", "b", "c")
s2.Add("b", "b1", "e", "f")
ss := s1.Merge(s2)
t.Assert(ss.Contains("a2"), true)
t.Assert(ss.Contains("b1"), true)
@ -255,7 +277,7 @@ func TestSet_Merge(t *testing.T) {
func TestSet_Sum(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2, 3, 4)
t.Assert(s1.Sum(), int(10))
})
@ -264,7 +286,7 @@ func TestSet_Sum(t *testing.T) {
func TestSet_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
s.Add(1).Add(2).Add(3).Add(4)
s.Add(1, 2, 3, 4)
t.Assert(s.Size(), 4)
t.AssertIN(s.Pop(), []int{1, 2, 3, 4})
t.Assert(s.Size(), 3)
@ -274,7 +296,7 @@ func TestSet_Pop(t *testing.T) {
func TestSet_Pops(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
s.Add(1).Add(2).Add(3).Add(4)
s.Add(1, 2, 3, 4)
t.Assert(s.Size(), 4)
t.Assert(s.Pops(0), nil)
t.AssertIN(s.Pops(1), []int{1, 2, 3, 4})
@ -324,43 +346,71 @@ func TestSet_Json(t *testing.T) {
})
}
func TestSet_AddIfNotExist(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.AddIfNotExist(1), false)
t.Assert(s.AddIfNotExist(2), true)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExist(2), false)
t.Assert(s.Contains(2), true)
})
}
func TestSet_AddIfNotExistFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
s.AddIfNotExistFunc(2, func() interface{} {
return 3
})
t.Assert(s.AddIfNotExistFunc(2, func() bool { return false }), false)
t.Assert(s.Contains(2), false)
t.Assert(s.Contains(3), true)
s.AddIfNotExistFunc(3, func() interface{} {
return 4
})
t.Assert(s.Contains(3), true)
t.Assert(s.Contains(4), false)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), true)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), false)
t.Assert(s.Contains(2), true)
})
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
r := s.AddIfNotExistFunc(1, func() bool {
time.Sleep(100 * time.Millisecond)
return true
})
t.Assert(r, false)
}()
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
wg.Wait()
})
}
s.AddIfNotExistFuncLock(2, func() interface{} {
return 3
})
t.Assert(s.Contains(2), false)
t.Assert(s.Contains(3), true)
s.AddIfNotExistFuncLock(3, func() interface{} {
return 4
})
t.Assert(s.Contains(3), true)
t.Assert(s.Contains(4), false)
func TestSet_AddIfNotExistFuncLock(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock(1, func() bool {
time.Sleep(500 * time.Millisecond)
return true
})
t.Assert(r, true)
}()
time.Sleep(100 * time.Millisecond)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock(1, func() bool {
return true
})
t.Assert(r, false)
}()
wg.Wait()
})
}

View File

@ -13,17 +13,39 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
"strings"
"sync"
"testing"
"time"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/test/gtest"
)
func TestIntSet_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var s gset.IntSet
s.Add(1, 1, 2)
s.Add([]int{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
t.AssertIN(2, s.Slice())
t.AssertIN(3, s.Slice())
t.AssertIN(4, s.Slice())
t.AssertNI(0, s.Slice())
t.Assert(s.Contains(4), true)
t.Assert(s.Contains(5), false)
s.Remove(1)
t.Assert(s.Size(), 3)
s.Clear()
t.Assert(s.Size(), 0)
})
}
func TestIntSet_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(1).Add(1).Add(2)
s.Add(1, 1, 2)
s.Add([]int{3, 4}...)
t.Assert(s.Size(), 4)
t.AssertIN(1, s.Slice())
@ -43,7 +65,7 @@ func TestIntSet_Basic(t *testing.T) {
func TestIntSet_Iterator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(1).Add(2).Add(3)
s.Add(1, 2, 3)
t.Assert(s.Size(), 3)
a1 := garray.New(true)
@ -64,7 +86,7 @@ func TestIntSet_Iterator(t *testing.T) {
func TestIntSet_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(1).Add(2).Add(3)
s.Add(1, 2, 3)
t.Assert(s.Size(), 3)
s.LockFunc(func(m map[int]struct{}) {
delete(m, 1)
@ -84,9 +106,9 @@ func TestIntSet_Equal(t *testing.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s3 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2, 3)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
t.Assert(s1.Equal(s2), true)
t.Assert(s1.Equal(s3), false)
})
@ -97,9 +119,9 @@ func TestIntSet_IsSubsetOf(t *testing.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s3 := gset.NewIntSet()
s1.Add(1).Add(2)
s2.Add(1).Add(2).Add(3)
s3.Add(1).Add(2).Add(3).Add(4)
s1.Add(1, 2)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
t.Assert(s1.IsSubsetOf(s2), true)
t.Assert(s2.IsSubsetOf(s3), true)
t.Assert(s1.IsSubsetOf(s3), true)
@ -112,8 +134,8 @@ func TestIntSet_Union(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2)
s2.Add(3).Add(4)
s1.Add(1, 2)
s2.Add(3, 4)
s3 := s1.Union(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(2), true)
@ -126,8 +148,8 @@ func TestIntSet_Diff(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Diff(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(2), true)
@ -140,8 +162,8 @@ func TestIntSet_Intersect(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Intersect(s2)
t.Assert(s3.Contains(1), false)
t.Assert(s3.Contains(2), false)
@ -154,8 +176,8 @@ func TestIntSet_Complement(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Complement(s2)
t.Assert(s3.Contains(1), false)
t.Assert(s3.Contains(2), false)
@ -167,7 +189,7 @@ func TestIntSet_Complement(t *testing.T) {
func TestIntSet_Size(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet(true)
s1.Add(1).Add(2).Add(3)
s1.Add(1, 2, 3)
t.Assert(s1.Size(), 3)
})
@ -178,8 +200,8 @@ func TestIntSet_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s2.Add(3).Add(4).Add(5)
s1.Add(1, 2, 3)
s2.Add(3, 4, 5)
s3 := s1.Merge(s2)
t.Assert(s3.Contains(1), true)
t.Assert(s3.Contains(5), true)
@ -190,7 +212,7 @@ func TestIntSet_Merge(t *testing.T) {
func TestIntSet_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s1.Add(1, 2, 3)
s3 := s1.Join(",")
t.Assert(strings.Contains(s3, "1"), true)
t.Assert(strings.Contains(s3, "2"), true)
@ -201,7 +223,7 @@ func TestIntSet_Join(t *testing.T) {
func TestIntSet_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s1.Add(1, 2, 3)
s3 := s1.String()
t.Assert(strings.Contains(s3, "["), true)
t.Assert(strings.Contains(s3, "]"), true)
@ -214,9 +236,9 @@ func TestIntSet_String(t *testing.T) {
func TestIntSet_Sum(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
s1.Add(1).Add(2).Add(3)
s1.Add(1, 2, 3)
s2 := gset.NewIntSet()
s2.Add(5).Add(6).Add(7)
s2.Add(5, 6, 7)
t.Assert(s2.Sum(), 18)
})
@ -226,7 +248,7 @@ func TestIntSet_Sum(t *testing.T) {
func TestIntSet_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(4).Add(2).Add(3)
s.Add(4, 2, 3)
t.Assert(s.Size(), 3)
t.AssertIN(s.Pop(), []int{4, 2, 3})
t.AssertIN(s.Pop(), []int{4, 2, 3})
@ -237,7 +259,7 @@ func TestIntSet_Pop(t *testing.T) {
func TestIntSet_Pops(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
s.Add(1).Add(4).Add(2).Add(3)
s.Add(1, 4, 2, 3)
t.Assert(s.Size(), 4)
t.Assert(s.Pops(0), nil)
t.AssertIN(s.Pops(1), []int{1, 4, 2, 3})
@ -258,6 +280,74 @@ func TestIntSet_Pops(t *testing.T) {
})
}
func TestIntSet_AddIfNotExist(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.AddIfNotExist(1), false)
t.Assert(s.AddIfNotExist(2), true)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExist(2), false)
t.Assert(s.Contains(2), true)
})
}
func TestIntSet_AddIfNotExistFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return false }), false)
t.Assert(s.Contains(2), false)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), true)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), false)
t.Assert(s.Contains(2), true)
})
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
r := s.AddIfNotExistFunc(1, func() bool {
time.Sleep(100 * time.Millisecond)
return true
})
t.Assert(r, false)
}()
s.Add(1)
wg.Wait()
})
}
func TestIntSet_AddIfNotExistFuncLock(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock(1, func() bool {
time.Sleep(500 * time.Millisecond)
return true
})
t.Assert(r, true)
}()
time.Sleep(100 * time.Millisecond)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock(1, func() bool {
return true
})
t.Assert(r, false)
}()
wg.Wait()
})
}
func TestIntSet_Json(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 3, 2, 4}
@ -287,46 +377,6 @@ func TestIntSet_Json(t *testing.T) {
})
}
func TestIntSet_AddIfNotExistFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
s.AddIfNotExistFunc(2, func() int {
return 3
})
t.Assert(s.Contains(2), false)
t.Assert(s.Contains(3), true)
s.AddIfNotExistFunc(3, func() int {
return 4
})
t.Assert(s.Contains(3), true)
t.Assert(s.Contains(4), false)
})
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet(true)
s.Add(1)
t.Assert(s.Contains(1), true)
t.Assert(s.Contains(2), false)
s.AddIfNotExistFuncLock(2, func() int {
return 3
})
t.Assert(s.Contains(2), false)
t.Assert(s.Contains(3), true)
s.AddIfNotExistFuncLock(3, func() int {
return 4
})
t.Assert(s.Contains(3), true)
t.Assert(s.Contains(4), false)
})
}
func TestIntSet_UnmarshalValue(t *testing.T) {
type V struct {
Name string

View File

@ -13,17 +13,39 @@ import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
"strings"
"sync"
"testing"
"time"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/test/gtest"
)
func TestStrSet_Var(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var s gset.StrSet
s.Add("1", "1", "2")
s.Add([]string{"3", "4"}...)
t.Assert(s.Size(), 4)
t.AssertIN("1", s.Slice())
t.AssertIN("2", s.Slice())
t.AssertIN("3", s.Slice())
t.AssertIN("4", s.Slice())
t.AssertNI("0", s.Slice())
t.Assert(s.Contains("4"), true)
t.Assert(s.Contains("5"), false)
s.Remove("1")
t.Assert(s.Size(), 3)
s.Clear()
t.Assert(s.Size(), 0)
})
}
func TestStrSet_Basic(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
s.Add("1").Add("1").Add("2")
s.Add("1", "1", "2")
s.Add([]string{"3", "4"}...)
t.Assert(s.Size(), 4)
t.AssertIN("1", s.Slice())
@ -43,7 +65,7 @@ func TestStrSet_Basic(t *testing.T) {
func TestStrSet_Iterator(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
s.Add("1").Add("2").Add("3")
s.Add("1", "2", "3")
t.Assert(s.Size(), 3)
a1 := garray.New(true)
@ -64,7 +86,7 @@ func TestStrSet_Iterator(t *testing.T) {
func TestStrSet_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet()
s.Add("1").Add("2").Add("3")
s.Add("1", "2", "3")
t.Assert(s.Size(), 3)
s.LockFunc(func(m map[string]struct{}) {
delete(m, "1")
@ -84,9 +106,9 @@ func TestStrSet_Equal(t *testing.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s3 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("1").Add("2").Add("3")
s3.Add("1").Add("2").Add("3").Add("4")
s1.Add("1", "2", "3")
s2.Add("1", "2", "3")
s3.Add("1", "2", "3", "4")
t.Assert(s1.Equal(s2), true)
t.Assert(s1.Equal(s3), false)
})
@ -97,9 +119,9 @@ func TestStrSet_IsSubsetOf(t *testing.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s3 := gset.NewStrSet()
s1.Add("1").Add("2")
s2.Add("1").Add("2").Add("3")
s3.Add("1").Add("2").Add("3").Add("4")
s1.Add("1", "2")
s2.Add("1", "2", "3")
s3.Add("1", "2", "3", "4")
t.Assert(s1.IsSubsetOf(s2), true)
t.Assert(s2.IsSubsetOf(s3), true)
t.Assert(s1.IsSubsetOf(s3), true)
@ -112,8 +134,8 @@ func TestStrSet_Union(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2")
s2.Add("3").Add("4")
s1.Add("1", "2")
s2.Add("3", "4")
s3 := s1.Union(s2)
t.Assert(s3.Contains("1"), true)
t.Assert(s3.Contains("2"), true)
@ -126,8 +148,8 @@ func TestStrSet_Diff(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s1.Add("1", "2", "3")
s2.Add("3", "4", "5")
s3 := s1.Diff(s2)
t.Assert(s3.Contains("1"), true)
t.Assert(s3.Contains("2"), true)
@ -140,8 +162,8 @@ func TestStrSet_Intersect(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s1.Add("1", "2", "3")
s2.Add("3", "4", "5")
s3 := s1.Intersect(s2)
t.Assert(s3.Contains("1"), false)
t.Assert(s3.Contains("2"), false)
@ -154,8 +176,8 @@ func TestStrSet_Complement(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s1.Add("1", "2", "3")
s2.Add("3", "4", "5")
s3 := s1.Complement(s2)
t.Assert(s3.Contains("1"), false)
t.Assert(s3.Contains("2"), false)
@ -179,8 +201,8 @@ func TestStrSet_Merge(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s1.Add("1").Add("2").Add("3")
s2.Add("3").Add("4").Add("5")
s1.Add("1", "2", "3")
s2.Add("3", "4", "5")
s3 := s1.Merge(s2)
t.Assert(s3.Contains("1"), true)
t.Assert(s3.Contains("6"), false)
@ -207,7 +229,7 @@ func TestStrSet_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
s1.Add("a").Add(`"b"`).Add(`\c`)
s1.Add("a", `"b"`, `\c`)
str1 := s1.Join(",")
t.Assert(strings.Contains(str1, `"b"`), true)
t.Assert(strings.Contains(str1, `\c`), true)
@ -225,7 +247,7 @@ func TestStrSet_String(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.New(true)
s1.Add("a").Add("a2").Add("b").Add("c")
s1.Add("a", "a2", "b", "c")
str1 := s1.String()
t.Assert(strings.Contains(str1, "["), true)
t.Assert(strings.Contains(str1, "]"), true)
@ -253,7 +275,7 @@ func TestStrSet_Size(t *testing.T) {
func TestStrSet_Remove(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSetFrom([]string{"a", "b", "c"}, true)
s1 = s1.Remove("b")
s1.Remove("b")
t.Assert(s1.Contains("b"), false)
t.Assert(s1.Contains("c"), true)
})
@ -294,6 +316,74 @@ func TestStrSet_Pops(t *testing.T) {
})
}
func TestStrSet_AddIfNotExist(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
s.Add("1")
t.Assert(s.Contains("1"), true)
t.Assert(s.AddIfNotExist("1"), false)
t.Assert(s.AddIfNotExist("2"), true)
t.Assert(s.Contains("2"), true)
t.Assert(s.AddIfNotExist("2"), false)
t.Assert(s.Contains("2"), true)
})
}
func TestStrSet_AddIfNotExistFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
s.Add("1")
t.Assert(s.Contains("1"), true)
t.Assert(s.Contains("2"), false)
t.Assert(s.AddIfNotExistFunc("2", func() bool { return false }), false)
t.Assert(s.Contains("2"), false)
t.Assert(s.AddIfNotExistFunc("2", func() bool { return true }), true)
t.Assert(s.Contains("2"), true)
t.Assert(s.AddIfNotExistFunc("2", func() bool { return true }), false)
t.Assert(s.Contains("2"), true)
})
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
r := s.AddIfNotExistFunc("1", func() bool {
time.Sleep(100 * time.Millisecond)
return true
})
t.Assert(r, false)
}()
s.Add("1")
wg.Wait()
})
}
func TestStrSet_AddIfNotExistFuncLock(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock("1", func() bool {
time.Sleep(500 * time.Millisecond)
return true
})
t.Assert(r, true)
}()
time.Sleep(100 * time.Millisecond)
go func() {
defer wg.Done()
r := s.AddIfNotExistFuncLock("1", func() bool {
return true
})
t.Assert(r, false)
}()
wg.Wait()
})
}
func TestStrSet_Json(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []string{"a", "b", "d", "c"}
@ -323,46 +413,6 @@ func TestStrSet_Json(t *testing.T) {
})
}
func TestStrSet_AddIfNotExistFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
s.Add("1")
t.Assert(s.Contains("1"), true)
t.Assert(s.Contains("2"), false)
s.AddIfNotExistFunc("2", func() string {
return "3"
})
t.Assert(s.Contains("2"), false)
t.Assert(s.Contains("3"), true)
s.AddIfNotExistFunc("3", func() string {
return "4"
})
t.Assert(s.Contains("3"), true)
t.Assert(s.Contains("4"), false)
})
gtest.C(t, func(t *gtest.T) {
s := gset.NewStrSet(true)
s.Add("1")
t.Assert(s.Contains("1"), true)
t.Assert(s.Contains("2"), false)
s.AddIfNotExistFuncLock("2", func() string {
return "3"
})
t.Assert(s.Contains("2"), false)
t.Assert(s.Contains("3"), true)
s.AddIfNotExistFuncLock("3", func() string {
return "4"
})
t.Assert(s.Contains("3"), true)
t.Assert(s.Contains("4"), false)
})
}
func TestStrSet_UnmarshalValue(t *testing.T) {
type V struct {
Name string

View File

@ -18,7 +18,7 @@ import (
// AVLTree holds elements of the AVL tree.
type AVLTree struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
root *AVLTreeNode
comparator func(v1, v2 interface{}) int
size int
@ -38,7 +38,7 @@ type AVLTreeNode struct {
// which is false in default.
func NewAVLTree(comparator func(v1, v2 interface{}) int, safe ...bool) *AVLTree {
return &AVLTree{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
comparator: comparator,
}
}
@ -55,7 +55,7 @@ func NewAVLTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{
}
// Clone returns a new tree with a copy of current tree.
func (tree *AVLTree) Clone(safe ...bool) *AVLTree {
func (tree *AVLTree) Clone() *AVLTree {
newTree := NewAVLTree(tree.comparator, !tree.mu.IsSafe())
newTree.Sets(tree.Map())
return newTree
@ -93,7 +93,7 @@ func (tree *AVLTree) Search(key interface{}) (value interface{}, found bool) {
func (tree *AVLTree) doSearch(key interface{}) (node *AVLTreeNode, found bool) {
node = tree.root
for node != nil {
cmp := tree.comparator(key, node.Key)
cmp := tree.getComparator()(key, node.Key)
switch {
case cmp == 0:
return node, true
@ -331,7 +331,7 @@ func (tree *AVLTree) Floor(key interface{}) (floor *AVLTreeNode, found bool) {
defer tree.mu.RUnlock()
n := tree.root
for n != nil {
c := tree.comparator(key, n.Key)
c := tree.getComparator()(key, n.Key)
switch {
case c == 0:
return n, true
@ -361,7 +361,7 @@ func (tree *AVLTree) Ceiling(key interface{}) (ceiling *AVLTreeNode, found bool)
defer tree.mu.RUnlock()
n := tree.root
for n != nil {
c := tree.comparator(key, n.Key)
c := tree.getComparator()(key, n.Key)
switch {
case c == 0:
return n, true
@ -465,7 +465,7 @@ func (tree *AVLTree) IteratorFrom(key interface{}, match bool, f func(key, value
tree.IteratorAscFrom(key, match, f)
}
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
// IteratorAsc iterates the tree readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *AVLTree) IteratorAsc(f func(key, value interface{}) bool) {
tree.mu.RLock()
@ -473,7 +473,7 @@ func (tree *AVLTree) IteratorAsc(f func(key, value interface{}) bool) {
tree.doIteratorAsc(tree.bottom(0), f)
}
// IteratorAscFrom iterates the tree in ascending order with given callback function <f>.
// IteratorAscFrom iterates the tree readonly in ascending order with given callback function <f>.
// The parameter <key> specifies the start entry for iterating. The <match> specifies whether
// starting iterating if the <key> is fully matched, or else using index searching iterating.
// If <f> returns true, then it continues iterating; or false to stop.
@ -499,7 +499,7 @@ func (tree *AVLTree) doIteratorAsc(node *AVLTreeNode, f func(key, value interfac
}
}
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// IteratorDesc iterates the tree readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *AVLTree) IteratorDesc(f func(key, value interface{}) bool) {
tree.mu.RLock()
@ -507,7 +507,7 @@ func (tree *AVLTree) IteratorDesc(f func(key, value interface{}) bool) {
tree.doIteratorDesc(tree.bottom(1), f)
}
// IteratorDescFrom iterates the tree in descending order with given callback function <f>.
// IteratorDescFrom iterates the tree readonly in descending order with given callback function <f>.
// The parameter <key> specifies the start entry for iterating. The <match> specifies whether
// starting iterating if the <key> is fully matched, or else using index searching iterating.
// If <f> returns true, then it continues iterating; or false to stop.
@ -541,7 +541,7 @@ func (tree *AVLTree) put(key interface{}, value interface{}, p *AVLTreeNode, qp
return true
}
c := tree.comparator(key, q.Key)
c := tree.getComparator()(key, q.Key)
if c == 0 {
q.Key = key
q.Value = value
@ -566,7 +566,7 @@ func (tree *AVLTree) remove(key interface{}, qp **AVLTreeNode) (value interface{
return nil, false
}
c := tree.comparator(key, q.Key)
c := tree.getComparator()(key, q.Key)
if c == 0 {
tree.size--
value = q.Value
@ -784,3 +784,12 @@ func output(node *AVLTreeNode, prefix string, isTail bool, str *string) {
func (tree *AVLTree) MarshalJSON() ([]byte, error) {
return json.Marshal(tree.Map())
}
// getComparator returns the comparator if it's previously set,
// or else it panics.
func (tree *AVLTree) getComparator() func(a, b interface{}) int {
if tree.comparator == nil {
panic("comparator is missing for tree")
}
return tree.comparator
}

View File

@ -20,7 +20,7 @@ import (
// BTree holds elements of the B-tree.
type BTree struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
root *BTreeNode
comparator func(v1, v2 interface{}) int
size int // Total number of keys in the tree
@ -50,7 +50,7 @@ func NewBTree(m int, comparator func(v1, v2 interface{}) int, safe ...bool) *BTr
}
return &BTree{
comparator: comparator,
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
m: m,
}
}
@ -67,7 +67,7 @@ func NewBTreeFrom(m int, comparator func(v1, v2 interface{}) int, data map[inter
}
// Clone returns a new tree with a copy of current tree.
func (tree *BTree) Clone(safe ...bool) *BTree {
func (tree *BTree) Clone() *BTree {
newTree := NewBTree(tree.m, tree.comparator, !tree.mu.IsSafe())
newTree.Sets(tree.Map())
return newTree
@ -406,7 +406,7 @@ func (tree *BTree) IteratorFrom(key interface{}, match bool, f func(key, value i
tree.IteratorAscFrom(key, match, f)
}
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
// IteratorAsc iterates the tree readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *BTree) IteratorAsc(f func(key, value interface{}) bool) {
tree.mu.RLock()
@ -418,7 +418,7 @@ func (tree *BTree) IteratorAsc(f func(key, value interface{}) bool) {
tree.doIteratorAsc(node, node.Entries[0], 0, f)
}
// IteratorAscFrom iterates the tree in ascending order with given callback function <f>.
// IteratorAscFrom iterates the tree readonly in ascending order with given callback function <f>.
// The parameter <key> specifies the start entry for iterating. The <match> specifies whether
// starting iterating if the <key> is fully matched, or else using index searching iterating.
// If <f> returns true, then it continues iterating; or false to stop.
@ -479,7 +479,7 @@ loop:
}
}
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// IteratorDesc iterates the tree readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *BTree) IteratorDesc(f func(key, value interface{}) bool) {
tree.mu.RLock()
@ -493,7 +493,7 @@ func (tree *BTree) IteratorDesc(f func(key, value interface{}) bool) {
tree.doIteratorDesc(node, entry, index, f)
}
// IteratorDescFrom iterates the tree in descending order with given callback function <f>.
// IteratorDescFrom iterates the tree readonly in descending order with given callback function <f>.
// The parameter <key> specifies the start entry for iterating. The <match> specifies whether
// starting iterating if the <key> is fully matched, or else using index searching iterating.
// If <f> returns true, then it continues iterating; or false to stop.
@ -510,7 +510,7 @@ func (tree *BTree) IteratorDescFrom(key interface{}, match bool, f func(key, val
}
}
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// IteratorDesc iterates the tree readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *BTree) doIteratorDesc(node *BTreeNode, entry *BTreeEntry, index int, f func(key, value interface{}) bool) {
first := true
@ -621,7 +621,7 @@ func (tree *BTree) search(node *BTreeNode, key interface{}) (index int, found bo
low, mid, high := 0, 0, len(node.Entries)-1
for low <= high {
mid = (high + low) / 2
compare := tree.comparator(key, node.Entries[mid].Key)
compare := tree.getComparator()(key, node.Entries[mid].Key)
switch {
case compare > 0:
low = mid + 1
@ -934,3 +934,12 @@ func (tree *BTree) deleteChild(node *BTreeNode, index int) {
func (tree *BTree) MarshalJSON() ([]byte, error) {
return json.Marshal(tree.Map())
}
// getComparator returns the comparator if it's previously set,
// or else it panics.
func (tree *BTree) getComparator() func(a, b interface{}) int {
if tree.comparator == nil {
panic("comparator is missing for tree")
}
return tree.comparator
}

View File

@ -24,7 +24,7 @@ const (
// RedBlackTree holds elements of the red-black tree.
type RedBlackTree struct {
mu *rwmutex.RWMutex
mu rwmutex.RWMutex
root *RedBlackTreeNode
size int
comparator func(v1, v2 interface{}) int
@ -45,7 +45,7 @@ type RedBlackTreeNode struct {
// which is false in default.
func NewRedBlackTree(comparator func(v1, v2 interface{}) int, safe ...bool) *RedBlackTree {
return &RedBlackTree{
mu: rwmutex.New(safe...),
mu: rwmutex.Create(safe...),
comparator: comparator,
}
}
@ -82,7 +82,7 @@ func (tree *RedBlackTree) SetComparator(comparator func(a, b interface{}) int) {
}
// Clone returns a new tree with a copy of current tree.
func (tree *RedBlackTree) Clone(safe ...bool) *RedBlackTree {
func (tree *RedBlackTree) Clone() *RedBlackTree {
newTree := NewRedBlackTree(tree.comparator, !tree.mu.IsSafe())
newTree.Sets(tree.Map())
return newTree
@ -109,14 +109,14 @@ 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.getComparator()(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)
compare := tree.getComparator()(key, node.Key)
switch {
case compare == 0:
//node.Key = key
@ -337,8 +337,10 @@ func (tree *RedBlackTree) Size() int {
// Keys returns all keys in asc order.
func (tree *RedBlackTree) Keys() []interface{} {
keys := make([]interface{}, tree.Size())
index := 0
var (
keys = make([]interface{}, tree.Size())
index = 0
)
tree.IteratorAsc(func(key, value interface{}) bool {
keys[index] = key
index++
@ -349,8 +351,10 @@ func (tree *RedBlackTree) Keys() []interface{} {
// Values returns all values in asc order based on the key.
func (tree *RedBlackTree) Values() []interface{} {
values := make([]interface{}, tree.Size())
index := 0
var (
values = make([]interface{}, tree.Size())
index = 0
)
tree.IteratorAsc(func(key, value interface{}) bool {
values[index] = value
index++
@ -440,7 +444,7 @@ func (tree *RedBlackTree) Floor(key interface{}) (floor *RedBlackTreeNode, found
defer tree.mu.RUnlock()
n := tree.root
for n != nil {
compare := tree.comparator(key, n.Key)
compare := tree.getComparator()(key, n.Key)
switch {
case compare == 0:
return n, true
@ -468,7 +472,7 @@ func (tree *RedBlackTree) Ceiling(key interface{}) (ceiling *RedBlackTreeNode, f
defer tree.mu.RUnlock()
n := tree.root
for n != nil {
compare := tree.comparator(key, n.Key)
compare := tree.getComparator()(key, n.Key)
switch {
case compare == 0:
return n, true
@ -495,7 +499,7 @@ func (tree *RedBlackTree) IteratorFrom(key interface{}, match bool, f func(key,
tree.IteratorAscFrom(key, match, f)
}
// IteratorAsc iterates the tree in ascending order with given callback function <f>.
// IteratorAsc iterates the tree readonly in ascending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *RedBlackTree) IteratorAsc(f func(key, value interface{}) bool) {
tree.mu.RLock()
@ -503,7 +507,7 @@ func (tree *RedBlackTree) IteratorAsc(f func(key, value interface{}) bool) {
tree.doIteratorAsc(tree.leftNode(), f)
}
// IteratorAscFrom iterates the tree in ascending order with given callback function <f>.
// IteratorAscFrom iterates the tree readonly in ascending order with given callback function <f>.
// The parameter <key> specifies the start entry for iterating. The <match> specifies whether
// starting iterating if the <key> is fully matched, or else using index searching iterating.
// If <f> returns true, then it continues iterating; or false to stop.
@ -539,14 +543,14 @@ loop:
old := node
for node.parent != nil {
node = node.parent
if tree.comparator(old.Key, node.Key) <= 0 {
if tree.getComparator()(old.Key, node.Key) <= 0 {
goto loop
}
}
}
}
// IteratorDesc iterates the tree in descending order with given callback function <f>.
// IteratorDesc iterates the tree readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
func (tree *RedBlackTree) IteratorDesc(f func(key, value interface{}) bool) {
tree.mu.RLock()
@ -554,7 +558,7 @@ func (tree *RedBlackTree) IteratorDesc(f func(key, value interface{}) bool) {
tree.doIteratorDesc(tree.rightNode(), f)
}
// IteratorDescFrom iterates the tree in descending order with given callback function <f>.
// IteratorDescFrom iterates the tree readonly in descending order with given callback function <f>.
// The parameter <key> specifies the start entry for iterating. The <match> specifies whether
// starting iterating if the <key> is fully matched, or else using index searching iterating.
// If <f> returns true, then it continues iterating; or false to stop.
@ -590,7 +594,7 @@ loop:
old := node
for node.parent != nil {
node = node.parent
if tree.comparator(old.Key, node.Key) >= 0 {
if tree.getComparator()(old.Key, node.Key) >= 0 {
goto loop
}
}
@ -699,7 +703,7 @@ func (tree *RedBlackTree) output(node *RedBlackTreeNode, prefix string, isTail b
func (tree *RedBlackTree) doSearch(key interface{}) (node *RedBlackTreeNode, found bool) {
node = tree.root
for node != nil {
compare := tree.comparator(key, node.Key)
compare := tree.getComparator()(key, node.Key)
switch {
case compare == 0:
return node, true
@ -927,12 +931,11 @@ func (tree *RedBlackTree) MarshalJSON() ([]byte, error) {
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
func (tree *RedBlackTree) UnmarshalJSON(b []byte) error {
if tree.mu == nil {
tree.mu = rwmutex.New()
tree.comparator = gutil.ComparatorString
}
tree.mu.Lock()
defer tree.mu.Unlock()
if tree.comparator == nil {
tree.comparator = gutil.ComparatorString
}
var data map[string]interface{}
if err := json.Unmarshal(b, &data); err != nil {
return err
@ -945,14 +948,22 @@ func (tree *RedBlackTree) UnmarshalJSON(b []byte) error {
// UnmarshalValue is an interface implement which sets any type of value for map.
func (tree *RedBlackTree) UnmarshalValue(value interface{}) (err error) {
if tree.mu == nil {
tree.mu = rwmutex.New()
tree.comparator = gutil.ComparatorString
}
tree.mu.Lock()
defer tree.mu.Unlock()
if tree.comparator == nil {
tree.comparator = gutil.ComparatorString
}
for k, v := range gconv.Map(value) {
tree.doSet(k, v)
}
return
}
// getComparator returns the comparator if it's previously set,
// or else it panics.
func (tree *RedBlackTree) getComparator() func(a, b interface{}) int {
if tree.comparator == nil {
panic("comparator is missing for tree")
}
return tree.comparator
}

View File

@ -54,11 +54,13 @@ func (v *Var) Clone() *Var {
// Set sets <value> to <v>, and returns the old value.
func (v *Var) Set(value interface{}) (old interface{}) {
if v.safe {
old = v.value.(*gtype.Interface).Set(value)
} else {
old = v.value
v.value = value
if t, ok := v.value.(*gtype.Interface); ok {
old = t.Set(value)
return
}
}
old = v.value
v.value = value
return
}
@ -68,7 +70,9 @@ func (v *Var) Val() interface{} {
return nil
}
if v.safe {
return v.value.(*gtype.Interface).Val()
if t, ok := v.value.(*gtype.Interface); ok {
return t.Val()
}
}
return v.value
}

View File

@ -11,6 +11,7 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/internal/intlog"
"time"
@ -18,7 +19,6 @@ import (
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/os/gcache"
"github.com/gogf/gf/util/grand"
)
@ -78,8 +78,8 @@ type DB interface {
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
// Model creation.
From(tables string) *Model
Table(tables string) *Model
Table(table ...string) *Model
Model(table ...string) *Model
Schema(schema string) *Schema
// Configuration methods.
@ -89,6 +89,7 @@ type DB interface {
SetSchema(schema string)
GetSchema() string
GetPrefix() string
GetGroup() string
SetDryRun(dryrun bool)
GetDryRun() bool
SetLogger(logger *glog.Logger)
@ -169,21 +170,23 @@ type Link interface {
Prepare(sql string) (*sql.Stmt, error)
}
// Value is the field value type.
type Value = *gvar.Var
type (
// Value is the field value type.
Value = *gvar.Var
// Record is the row record of the table.
type Record map[string]Value
// Record is the row record of the table.
Record map[string]Value
// Result is the row record array.
type Result []Record
// Result is the row record array.
Result []Record
// Map is alias of map[string]interface{},
// which is the most common usage map type.
type Map = map[string]interface{}
// Map is alias of map[string]interface{},
// which is the most common usage map type.
Map = map[string]interface{}
// List is type of map array.
type List = []Map
// List is type of map array.
List = []Map
)
const (
gINSERT_OPTION_DEFAULT = 0

View File

@ -11,6 +11,7 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/internal/utils"
"reflect"
"regexp"
"strings"
@ -157,7 +158,7 @@ func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) {
return c.DB.DoGetAll(nil, sql, args...)
}
// doGetAll queries and returns data records from database.
// DoGetAll queries and returns data records from database.
func (c *Core) DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) {
if link == nil {
link, err = c.DB.Slave()
@ -379,13 +380,15 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e
// 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one;
// 3: ignore: if there's unique/primary key in the data, it ignores the inserting;
func (c *Core) DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) {
var fields []string
var values []string
var params []interface{}
var dataMap Map
table = c.DB.QuotePrefixTableName(table)
reflectValue := reflect.ValueOf(data)
reflectKind := reflectValue.Kind()
var (
fields []string
values []string
params []interface{}
dataMap Map
reflectValue = reflect.ValueOf(data)
reflectKind = reflectValue.Kind()
)
if reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
@ -401,16 +404,23 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
if len(dataMap) == 0 {
return nil, errors.New("data cannot be empty")
}
charL, charR := c.DB.GetChars()
var (
charL, charR = c.DB.GetChars()
operation = GetInsertOperationByOption(option)
updateStr = ""
)
for k, v := range dataMap {
fields = append(fields, charL+k+charR)
values = append(values, "?")
params = append(params, v)
}
operation := GetInsertOperationByOption(option)
updateStr := ""
if option == gINSERT_OPTION_SAVE {
for k, _ := range dataMap {
// If it's SAVE operation,
// do not automatically update the creating time.
if utils.EqualFoldWithoutChars(k, gSOFT_FIELD_NAME_CREATE) {
continue
}
if len(updateStr) > 0 {
updateStr += ","
}
@ -462,12 +472,15 @@ func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Resu
return c.DB.DoBatchInsert(nil, table, list, gINSERT_OPTION_SAVE, batch...)
}
// doBatchInsert batch inserts/replaces/saves data.
// DoBatchInsert batch inserts/replaces/saves data.
func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
var keys, values []string
var params []interface{}
table = c.DB.QuotePrefixTableName(table)
listMap := (List)(nil)
var (
keys []string
values []string
params []interface{}
listMap List
)
switch v := list.(type) {
case Result:
listMap = v.List()
@ -478,8 +491,10 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
case Map:
listMap = List{v}
default:
rv := reflect.ValueOf(list)
kind := rv.Kind()
var (
rv = reflect.ValueOf(list)
kind = rv.Kind()
)
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
@ -492,7 +507,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
listMap[i] = DataToMapDeep(rv.Index(i).Interface())
}
case reflect.Map, reflect.Struct:
listMap = List{DataToMapDeep(list)}
listMap = List{DataToMapDeep(v)}
default:
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
}
@ -512,15 +527,21 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
holders = append(holders, "?")
}
// Prepare the batch result pointer.
batchResult := new(SqlResult)
charL, charR := c.DB.GetChars()
keysStr := charL + strings.Join(keys, charR+","+charL) + charR
valueHolderStr := "(" + strings.Join(holders, ",") + ")"
operation := GetInsertOperationByOption(option)
updateStr := ""
var (
charL, charR = c.DB.GetChars()
batchResult = new(SqlResult)
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
valueHolderStr = "(" + strings.Join(holders, ",") + ")"
operation = GetInsertOperationByOption(option)
updateStr = ""
)
if option == gINSERT_OPTION_SAVE {
for _, k := range keys {
// If it's SAVE operation,
// do not automatically update the creating time.
if utils.EqualFoldWithoutChars(k, gSOFT_FIELD_NAME_CREATE) {
continue
}
if len(updateStr) > 0 {
updateStr += ","
}
@ -599,18 +620,25 @@ func (c *Core) Update(table string, data interface{}, condition interface{}, arg
// Also see Update.
func (c *Core) DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
table = c.DB.QuotePrefixTableName(table)
updates := ""
rv := reflect.ValueOf(data)
kind := rv.Kind()
var (
rv = reflect.ValueOf(data)
kind = rv.Kind()
)
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
params := []interface{}(nil)
var (
params []interface{}
updates = ""
)
switch kind {
case reflect.Map, reflect.Struct:
var fields []string
for k, v := range DataToMapDeep(data) {
var (
fields []string
dataMap = DataToMapDeep(data)
)
for k, v := range dataMap {
fields = append(fields, c.DB.QuoteWord(k)+"=?")
params = append(params, v)
}
@ -656,7 +684,7 @@ func (c *Core) Delete(table string, condition interface{}, args ...interface{})
return c.DB.DoDelete(nil, table, newWhere, newArgs...)
}
// doDelete does "DELETE FROM ... " statement for the table.
// DoDelete does "DELETE FROM ... " statement for the table.
// Also see Delete.
func (c *Core) DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) {
if link == nil {

View File

@ -174,6 +174,11 @@ func (c *Core) GetPrefix() string {
return c.prefix
}
// GetGroup returns the group string configured.
func (c *Core) GetGroup() string {
return c.group
}
// SetDryRun enables/disables the DryRun feature.
func (c *Core) SetDryRun(dryrun bool) {
c.dryrun.Set(dryrun)

View File

@ -13,6 +13,7 @@ package gdb
import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/text/gstr"
@ -187,9 +188,10 @@ func (d *DriverMssql) Tables(schema ...string) (tables []string, err error) {
// TableFields retrieves and returns the fields information of specified table of current schema.
func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
return nil, errors.New("function TableFields supports only single table operations")
}
checkSchema := d.DB.GetSchema()
if len(schema) > 0 && schema[0] != "" {

View File

@ -8,6 +8,7 @@ package gdb
import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/text/gregex"
@ -89,9 +90,10 @@ func (d *DriverMysql) Tables(schema ...string) (tables []string, err error) {
//
// It's using cache feature to enhance the performance, which is never expired util the process restarts.
func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
return nil, errors.New("function TableFields supports only single table operations")
}
checkSchema := d.schema.Val()
if len(schema) > 0 && schema[0] != "" {

View File

@ -148,9 +148,10 @@ func (d *DriverOracle) Tables(schema ...string) (tables []string, err error) {
// TableFields retrieves and returns the fields information of specified table of current schema.
func (d *DriverOracle) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
return nil, errors.New("function TableFields supports only single table operations")
}
checkSchema := d.DB.GetSchema()
if len(schema) > 0 && schema[0] != "" {

View File

@ -13,6 +13,7 @@ package gdb
import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/text/gstr"
@ -78,7 +79,6 @@ func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) {
if err != nil {
return nil, err
}
query := "SELECT TABLENAME FROM PG_TABLES WHERE SCHEMANAME = 'public' ORDER BY TABLENAME"
if len(schema) > 0 && schema[0] != "" {
query = fmt.Sprintf("SELECT TABLENAME FROM PG_TABLES WHERE SCHEMANAME = '%s' ORDER BY TABLENAME", schema[0])
@ -97,9 +97,10 @@ func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) {
// TableFields retrieves and returns the fields information of specified table of current schema.
func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
return nil, errors.New("function TableFields supports only single table operations")
}
table, _ = gregex.ReplaceString("\"", "", table)
checkSchema := d.DB.GetSchema()

View File

@ -12,6 +12,7 @@ package gdb
import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/os/gfile"
@ -88,11 +89,11 @@ func (d *DriverSqlite) Tables(schema ...string) (tables []string, err error) {
// TableFields retrieves and returns the fields information of specified table of current schema.
func (d *DriverSqlite) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
table = gstr.Trim(table)
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
panic("function TableFields supports only single table operations")
return nil, errors.New("function TableFields supports only single table operations")
}
checkSchema := d.DB.GetSchema()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]

View File

@ -147,8 +147,13 @@ func doQuoteWord(s, charLeft, charRight string) string {
}
// doQuoteString quotes string with quote chars. It handles strings like:
// "user", "user u", "user,user_detail", "user u, user_detail ut",
// "user.user u, user.user_detail ut", "u.id asc".
// "user",
// "user u",
// "user,user_detail",
// "user u, user_detail ut",
// "user.user u, user.user_detail ut",
// "u.id, u.name, u.age",
// "u.id asc".
func doQuoteString(s, charLeft, charRight string) string {
array1 := gstr.SplitAndTrim(s, ",")
for k1, v1 := range array1 {
@ -201,8 +206,13 @@ func GetPrimaryKey(pointer interface{}) string {
// GetPrimaryKeyCondition returns a new where condition by primary field name.
// The optional parameter <where> is like follows:
// 123, []int{1, 2, 3}, "john", []string{"john", "smith"}
// g.Map{"id": g.Slice{1,2,3}}, g.Map{"id": 1, "name": "john"}, etc.
// 123
// []int{1, 2, 3}
// "john"
// []string{"john", "smith"}
// g.Map{"id": g.Slice{1,2,3}}
// g.Map{"id": 1, "name": "john"}
// etc.
//
// Note that it returns the given <where> parameter directly if there's the <primary> is empty.
func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondition []interface{}) {
@ -443,11 +453,23 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
newArgs = append(newArgs, arg)
continue
}
// It converts the struct to string in default
// if it implements the String interface.
if v, ok := arg.(apiString); ok {
switch v := arg.(type) {
case time.Time, *time.Time:
newArgs = append(newArgs, arg)
continue
// Special handling for gtime.Time.
case gtime.Time:
newArgs = append(newArgs, v.String())
continue
default:
// It converts the struct to string in default
// if it implements the String interface.
if v, ok := arg.(apiString); ok {
newArgs = append(newArgs, v.String())
continue
}
}
newArgs = append(newArgs, arg)

View File

@ -7,6 +7,8 @@
package gdb
import (
"fmt"
"github.com/gogf/gf/text/gregex"
"time"
"github.com/gogf/gf/text/gstr"
@ -26,6 +28,7 @@ type Model struct {
whereHolder []*whereHolder // Condition strings for where operation.
groupBy string // Used for "group by" statement.
orderBy string // Used for "order by" statement.
having []interface{} // Used for "having..." statement.
start int // Used for "select ... start, limit ..." statement.
limit int // Used for "select ... start, limit ..." statement.
option int // Option for extra operation features.
@ -37,6 +40,7 @@ type Model struct {
cacheEnabled bool // Enable sql result cache feature.
cacheDuration time.Duration // Cache TTL duration.
cacheName string // Cache name for custom operation.
unscoped bool // Disables soft deleting features when select/delete operations.
safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model.
}
@ -58,70 +62,70 @@ const (
)
// Table creates and returns a new ORM model from given schema.
// The parameter <tables> can be more than one table names, like :
// "user", "user u", "user, user_detail", "user u, user_detail ud"
func (c *Core) Table(table string) *Model {
table = c.DB.QuotePrefixTableName(table)
// The parameter <table> can be more than one table names, and also alias name, like:
// 1. Table names:
// Table("user")
// Table("user u")
// Table("user, user_detail")
// Table("user u, user_detail ud")
// 2. Table name with alias: Table("user", "u")
func (c *Core) Table(table ...string) *Model {
tables := ""
if len(table) > 1 {
tables = fmt.Sprintf(
`%s AS %s`, c.DB.QuotePrefixTableName(table[0]), c.DB.QuoteWord(table[1]),
)
} else if len(table) == 1 {
tables = c.DB.QuotePrefixTableName(table[0])
} else {
panic("table cannot be empty")
}
return &Model{
db: c.DB,
tablesInit: table,
tables: table,
tablesInit: tables,
tables: tables,
fields: "*",
start: -1,
offset: -1,
safe: true,
option: OPTION_ALLOWEMPTY,
}
}
// Model is alias of Core.Table.
// See Core.Table.
func (c *Core) Model(table string) *Model {
return c.DB.Table(table)
}
// From is alias of Core.Table.
// See Core.Table.
// Deprecated.
func (c *Core) From(table string) *Model {
return c.DB.Table(table)
func (c *Core) Model(table ...string) *Model {
return c.DB.Table(table...)
}
// Table acts like Core.Table except it operates on transaction.
// See Core.Table.
func (tx *TX) Table(table string) *Model {
table = tx.db.QuotePrefixTableName(table)
return &Model{
db: tx.db,
tx: tx,
tablesInit: table,
tables: table,
fields: "*",
start: -1,
offset: -1,
safe: true,
option: OPTION_ALLOWEMPTY,
}
func (tx *TX) Table(table ...string) *Model {
model := tx.db.Table(table...)
model.db = tx.db
model.tx = tx
return model
}
// Model is alias of tx.Table.
// See tx.Table.
func (tx *TX) Model(table string) *Model {
return tx.Table(table)
}
// From is alias of tx.Table.
// See tx.Table.
// Deprecated.
func (tx *TX) From(table string) *Model {
return tx.Table(table)
func (tx *TX) Model(table ...string) *Model {
return tx.Table(table...)
}
// As sets an alias name for current table.
func (m *Model) As(as string) *Model {
if m.tables != "" {
model := m.getModel()
model.tables = gstr.TrimRight(model.tables) + " AS " + as
split := " JOIN "
if gstr.Contains(model.tables, split) {
// For join table.
array := gstr.Split(model.tables, split)
array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1])
model.tables = gstr.Join(array, split)
} else {
// For base table.
model.tables = gstr.TrimRight(model.tables) + " AS " + as
}
return model
}
return m

View File

@ -6,7 +6,10 @@
package gdb
import "github.com/gogf/gf/util/gconv"
import (
"github.com/gogf/gf/util/gconv"
"strings"
)
// Where sets the condition statement for the model. The parameter <where> can be type of
// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
@ -32,6 +35,17 @@ func (m *Model) Where(where interface{}, args ...interface{}) *Model {
return model
}
// Having sets the having statement for the model.
// The parameters of this function usage are as the same as function Where.
// See Where.
func (m *Model) Having(having interface{}, args ...interface{}) *Model {
model := m.getModel()
model.having = []interface{}{
having, args,
}
return model
}
// WherePri does the same logic as Model.Where except that if the parameter <where>
// is a single condition like int/string/float/slice, it treats the condition as the primary
// key value. That is, if primary key is "id" and given <where> parameter as "123", the
@ -87,9 +101,9 @@ func (m *Model) GroupBy(groupBy string) *Model {
}
// Order sets the "ORDER BY" statement for the model.
func (m *Model) Order(orderBy string) *Model {
func (m *Model) Order(orderBy ...string) *Model {
model := m.getModel()
model.orderBy = m.db.QuoteString(orderBy)
model.orderBy = m.db.QuoteString(strings.Join(orderBy, " "))
return model
}

View File

@ -8,8 +8,21 @@ package gdb
import (
"database/sql"
"fmt"
"github.com/gogf/gf/os/gtime"
)
// Unscoped enables/disables the soft deleting feature.
func (m *Model) Unscoped(unscoped ...bool) *Model {
model := m.getModel()
if len(unscoped) > 0 {
model.unscoped = unscoped[0]
} else {
model.unscoped = true
}
return model
}
// Delete does "DELETE FROM ... " statement for the model.
// The optional parameter <where> is the same as the parameter of Model.Where function,
// see Model.Where.
@ -22,6 +35,19 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
m.checkAndRemoveCache()
}
}()
condition, conditionArgs := m.formatCondition(false)
return m.db.DoDelete(m.getLink(true), m.tables, condition, conditionArgs...)
var (
fieldNameDelete = m.getSoftFieldNameDelete()
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
)
// Soft deleting.
if !m.unscoped && fieldNameDelete != "" {
return m.db.DoUpdate(
m.getLink(true),
m.tables,
fmt.Sprintf(`%s='%s'`, m.db.QuoteString(fieldNameDelete), gtime.Now().String()),
conditionWhere+conditionExtra,
conditionArgs...,
)
}
return m.db.DoDelete(m.getLink(true), m.tables, conditionWhere+conditionExtra, conditionArgs...)
}

View File

@ -9,8 +9,10 @@ package gdb
import (
"database/sql"
"errors"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/gutil"
"reflect"
)
@ -94,6 +96,9 @@ func (m *Model) Data(data ...interface{}) *Model {
// The optional parameter <data> is the same as the parameter of Model.Data function,
// see Model.Data.
func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
if len(data) > 0 {
return m.Data(data...).Insert()
}
return m.doInsertWithOption(gINSERT_OPTION_DEFAULT, data...)
}
@ -101,45 +106,10 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
// The optional parameter <data> is the same as the parameter of Model.Data function,
// see Model.Data.
func (m *Model) InsertIgnore(data ...interface{}) (result sql.Result, err error) {
return m.doInsertWithOption(gINSERT_OPTION_IGNORE, data...)
}
// doInsertWithOption inserts data with option parameter.
func (m *Model) doInsertWithOption(option int, data ...interface{}) (result sql.Result, err error) {
if len(data) > 0 {
return m.Data(data...).Insert()
}
defer func() {
if err == nil {
m.checkAndRemoveCache()
}
}()
if m.data == nil {
return nil, errors.New("inserting into table with empty data")
}
if list, ok := m.data.(List); ok {
// Batch insert.
batch := 10
if m.batch > 0 {
batch = m.batch
}
return m.db.DoBatchInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(list),
option,
batch,
)
} else if data, ok := m.data.(Map); ok {
// Single insert.
return m.db.DoInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(data),
option,
)
}
return nil, errors.New("inserting into table with invalid data type")
return m.doInsertWithOption(gINSERT_OPTION_IGNORE, data...)
}
// Replace does "REPLACE INTO ..." statement for the model.
@ -149,37 +119,7 @@ func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) {
if len(data) > 0 {
return m.Data(data...).Replace()
}
defer func() {
if err == nil {
m.checkAndRemoveCache()
}
}()
if m.data == nil {
return nil, errors.New("replacing into table with empty data")
}
if list, ok := m.data.(List); ok {
// Batch replace.
batch := 10
if m.batch > 0 {
batch = m.batch
}
return m.db.DoBatchInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(list),
gINSERT_OPTION_REPLACE,
batch,
)
} else if data, ok := m.data.(Map); ok {
// Single insert.
return m.db.DoInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(data),
gINSERT_OPTION_REPLACE,
)
}
return nil, errors.New("replacing into table with invalid data type")
return m.doInsertWithOption(gINSERT_OPTION_REPLACE, data...)
}
// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the model.
@ -192,35 +132,70 @@ func (m *Model) Save(data ...interface{}) (result sql.Result, err error) {
if len(data) > 0 {
return m.Data(data...).Save()
}
return m.doInsertWithOption(gINSERT_OPTION_SAVE, data...)
}
// doInsertWithOption inserts data with option parameter.
func (m *Model) doInsertWithOption(option int, data ...interface{}) (result sql.Result, err error) {
defer func() {
if err == nil {
m.checkAndRemoveCache()
}
}()
if m.data == nil {
return nil, errors.New("saving into table with empty data")
return nil, errors.New("inserting into table with empty data")
}
var (
nowString = gtime.Now().String()
fieldNameCreate = m.getSoftFieldNameCreate()
fieldNameUpdate = m.getSoftFieldNameUpdate()
fieldNameDelete = m.getSoftFieldNameDelete()
)
// Batch operation.
if list, ok := m.data.(List); ok {
// Batch save.
batch := gDEFAULT_BATCH_NUM
if m.batch > 0 {
batch = m.batch
}
// Automatic handling for creating/updating time.
if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") {
for k, v := range list {
gutil.MapDelete(v, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
if fieldNameCreate != "" {
v[fieldNameCreate] = nowString
}
if fieldNameUpdate != "" {
v[fieldNameUpdate] = nowString
}
list[k] = v
}
}
return m.db.DoBatchInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(list),
gINSERT_OPTION_SAVE,
option,
batch,
)
} else if data, ok := m.data.(Map); ok {
// Single save.
}
// Single operation.
if data, ok := m.data.(Map); ok {
// Automatic handling for creating/updating time.
if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") {
gutil.MapDelete(data, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
if fieldNameCreate != "" {
data[fieldNameCreate] = nowString
}
if fieldNameUpdate != "" {
data[fieldNameUpdate] = nowString
}
}
return m.db.DoInsert(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(data),
gINSERT_OPTION_SAVE,
option,
)
}
return nil, errors.New("saving into table with invalid data type")
return nil, errors.New("inserting into table with invalid data type")
}

View File

@ -9,22 +9,70 @@ package gdb
import "fmt"
// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
func (m *Model) LeftJoin(table string, on string) *Model {
// The parameter <table> can be joined table and its joined condition,
// and also with its alias name, like:
// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
func (m *Model) LeftJoin(table ...string) *Model {
model := m.getModel()
model.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", m.db.QuotePrefixTableName(table), on)
if len(table) > 2 {
model.tables += fmt.Sprintf(
" LEFT JOIN %s AS %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), m.db.QuoteWord(table[1]), table[2],
)
} else if len(table) == 2 {
model.tables += fmt.Sprintf(
" LEFT JOIN %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), table[1],
)
} else {
panic("invalid join table parameter")
}
return model
}
// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
func (m *Model) RightJoin(table string, on string) *Model {
// The parameter <table> can be joined table and its joined condition,
// and also with its alias name, like:
// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
func (m *Model) RightJoin(table ...string) *Model {
model := m.getModel()
model.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", m.db.QuotePrefixTableName(table), on)
if len(table) > 2 {
model.tables += fmt.Sprintf(
" RIGHT JOIN %s AS %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), m.db.QuoteWord(table[1]), table[2],
)
} else if len(table) == 2 {
model.tables += fmt.Sprintf(
" RIGHT JOIN %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), table[1],
)
} else {
panic("invalid join table parameter")
}
return model
}
// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
func (m *Model) InnerJoin(table string, on string) *Model {
// The parameter <table> can be joined table and its joined condition,
// and also with its alias name, like:
// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
func (m *Model) InnerJoin(table ...string) *Model {
model := m.getModel()
model.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", m.db.QuotePrefixTableName(table), on)
if len(table) > 2 {
model.tables += fmt.Sprintf(
" INNER JOIN %s AS %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), m.db.QuoteWord(table[1]), table[2],
)
} else if len(table) == 2 {
model.tables += fmt.Sprintf(
" INNER JOIN %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), table[1],
)
} else {
panic("invalid join table parameter")
}
return model
}

View File

@ -30,9 +30,25 @@ func (m *Model) All(where ...interface{}) (Result, error) {
if len(where) > 0 {
return m.Where(where[0], where[1:]...).All()
}
condition, conditionArgs := m.formatCondition(false)
var (
softDeletingCondition = m.getConditionForSoftDeleting()
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
)
if !m.unscoped && softDeletingCondition != "" {
if conditionWhere == "" {
conditionWhere = " WHERE "
} else {
conditionWhere += " AND "
}
conditionWhere += softDeletingCondition
}
return m.getAll(
fmt.Sprintf("SELECT %s FROM %s%s", m.fields, m.tables, condition),
fmt.Sprintf(
"SELECT %s FROM %s%s",
m.db.QuoteString(m.fields),
m.tables,
conditionWhere+conditionExtra,
),
conditionArgs...,
)
}
@ -73,8 +89,7 @@ func (m *Model) One(where ...interface{}) (Record, error) {
if len(where) > 0 {
return m.Where(where[0], where[1:]...).One()
}
condition, conditionArgs := m.formatCondition(true)
all, err := m.getAll(fmt.Sprintf("SELECT %s FROM %s%s", m.fields, m.tables, condition), conditionArgs...)
all, err := m.All()
if err != nil {
return nil, err
}
@ -234,10 +249,22 @@ func (m *Model) Count(where ...interface{}) (int, error) {
}
countFields := "COUNT(1)"
if m.fields != "" && m.fields != "*" {
countFields = fmt.Sprintf(`COUNT(%s)`, m.fields)
countFields = fmt.Sprintf(`COUNT(%s)`, m.db.QuoteString(m.fields))
}
condition, conditionArgs := m.formatCondition(false)
s := fmt.Sprintf("SELECT %s FROM %s %s", countFields, m.tables, condition)
var (
softDeletingCondition = m.getConditionForSoftDeleting()
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
)
if !m.unscoped && softDeletingCondition != "" {
if conditionWhere == "" {
conditionWhere = " WHERE "
} else {
conditionWhere += " AND "
}
conditionWhere += softDeletingCondition
}
s := fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra)
if len(m.groupBy) > 0 {
s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s)
}

View File

@ -0,0 +1,147 @@
// Copyright 2020 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 gdb
import (
"fmt"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/gutil"
)
const (
gSOFT_FIELD_NAME_CREATE = "create_at"
gSOFT_FIELD_NAME_UPDATE = "update_at"
gSOFT_FIELD_NAME_DELETE = "delete_at"
)
// getSoftFieldNameCreate checks and returns the field name for record creating time.
// If there's no field name for storing creating time, it returns an empty string.
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
func (m *Model) getSoftFieldNameCreate(table ...string) string {
name := ""
if len(table) > 0 {
name = table[0]
} else {
name = m.getPrimaryTableName()
}
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_CREATE)
}
// getSoftFieldNameUpdate checks and returns the field name for record updating time.
// If there's no field name for storing updating time, it returns an empty string.
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
func (m *Model) getSoftFieldNameUpdate(table ...string) (field string) {
name := ""
if len(table) > 0 {
name = table[0]
} else {
name = m.getPrimaryTableName()
}
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_UPDATE)
}
// getSoftFieldNameDelete checks and returns the field name for record deleting time.
// If there's no field name for storing deleting time, it returns an empty string.
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
func (m *Model) getSoftFieldNameDelete(table ...string) (field string) {
name := ""
if len(table) > 0 {
name = table[0]
} else {
name = m.getPrimaryTableName()
}
return m.getSoftFieldName(name, gSOFT_FIELD_NAME_DELETE)
}
// getSoftFieldName retrieves and returns the field name of the table for possible key.
func (m *Model) getSoftFieldName(table string, key string) (field string) {
fieldsMap, _ := m.db.TableFields(table)
if len(fieldsMap) > 0 {
field, _ = gutil.MapPossibleItemByKey(
gconv.Map(fieldsMap), key,
)
}
return
}
// getConditionForSoftDeleting retrieves and returns the condition string for soft deleting.
// It supports multiple tables string like:
// "user u, user_detail ud"
// "user u LEFT JOIN user_detail ud ON(ud.uid=u.uid)"
// "user LEFT JOIN user_detail ON(user_detail.uid=user.uid)"
// "user u LEFT JOIN user_detail ud ON(ud.uid=u.uid) LEFT JOIN user_stats us ON(us.uid=u.uid)"
func (m *Model) getConditionForSoftDeleting() string {
if m.unscoped {
return ""
}
conditionArray := garray.NewStrArray()
if gstr.Contains(m.tables, " JOIN ") {
// Base table.
match, _ := gregex.MatchString(`(.+?) [A-Z]+ JOIN`, m.tables)
conditionArray.Append(m.getConditionOfTableStringForSoftDeleting(match[1]))
// Multiple joined tables.
matches, _ := gregex.MatchAllString(`JOIN (.+?) ON`, m.tables)
for _, match := range matches {
conditionArray.Append(m.getConditionOfTableStringForSoftDeleting(match[1]))
}
}
if conditionArray.Len() == 0 && gstr.Contains(m.tables, ",") {
// Multiple base tables.
for _, s := range gstr.SplitAndTrim(m.tables, ",") {
conditionArray.Append(m.getConditionOfTableStringForSoftDeleting(s))
}
}
conditionArray.FilterEmpty()
if conditionArray.Len() > 0 {
return conditionArray.Join(" AND ")
}
// Only one table.
if fieldName := m.getSoftFieldNameDelete(); fieldName != "" {
return fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(fieldName))
}
return ""
}
// getConditionOfTableStringForSoftDeleting does something as its name describes.
func (m *Model) getConditionOfTableStringForSoftDeleting(s string) string {
var (
field = ""
table = ""
array1 = gstr.SplitAndTrim(s, " ")
array2 = gstr.SplitAndTrim(array1[0], ".")
)
if len(array2) >= 2 {
table = array2[1]
} else {
table = array2[0]
}
field = m.getSoftFieldNameDelete(table)
if field == "" {
return ""
}
if len(array1) >= 3 {
return fmt.Sprintf(`%s.%s IS NULL`, m.db.QuoteWord(array1[2]), m.db.QuoteWord(field))
}
if len(array1) >= 2 {
return fmt.Sprintf(`%s.%s IS NULL`, m.db.QuoteWord(array1[1]), m.db.QuoteWord(field))
}
return fmt.Sprintf(`%s.%s IS NULL`, m.db.QuoteWord(table), m.db.QuoteWord(field))
}
// getPrimaryTableName parses and returns the primary table name.
func (m *Model) getPrimaryTableName() string {
array1 := gstr.SplitAndTrim(m.tables, ",")
array2 := gstr.SplitAndTrim(array1[0], " ")
array3 := gstr.SplitAndTrim(array2[0], ".")
if len(array3) >= 2 {
return array3[1]
}
return array3[0]
}

View File

@ -9,6 +9,12 @@ package gdb
import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/gutil"
"reflect"
)
// Update does "UPDATE ... " statement for the model.
@ -34,12 +40,44 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
if m.data == nil {
return nil, errors.New("updating table with empty data")
}
condition, conditionArgs := m.formatCondition(false)
var (
updateData = m.data
fieldNameCreate = m.getSoftFieldNameCreate()
fieldNameUpdate = m.getSoftFieldNameUpdate()
fieldNameDelete = m.getSoftFieldNameDelete()
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
)
// Automatically update the record updating time.
if !m.unscoped && fieldNameUpdate != "" {
var (
refValue = reflect.ValueOf(m.data)
refKind = refValue.Kind()
)
if refKind == reflect.Ptr {
refValue = refValue.Elem()
refKind = refValue.Kind()
}
switch refKind {
case reflect.Map, reflect.Struct:
dataMap := DataToMapDeep(m.data)
gutil.MapDelete(dataMap, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
if fieldNameUpdate != "" {
dataMap[fieldNameUpdate] = gtime.Now().String()
}
updateData = dataMap
default:
updates := gconv.String(m.data)
if fieldNameUpdate != "" && !gstr.Contains(updates, fieldNameUpdate) {
updates += fmt.Sprintf(`,%s='%s'`, fieldNameUpdate, gtime.Now().String())
}
updateData = updates
}
}
return m.db.DoUpdate(
m.getLink(true),
m.tables,
m.filterDataForInsertOrUpdate(m.data),
condition,
m.filterDataForInsertOrUpdate(updateData),
conditionWhere+conditionExtra,
m.mergeArguments(conditionArgs)...,
)
}

View File

@ -12,6 +12,7 @@ import (
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"time"
)
@ -28,15 +29,19 @@ func (m *Model) getModel() *Model {
// filterDataForInsertOrUpdate does filter feature with data for inserting/updating operations.
// Note that, it does not filter list item, which is also type of map, for "omit empty" feature.
func (m *Model) filterDataForInsertOrUpdate(data interface{}) interface{} {
if list, ok := m.data.(List); ok {
for k, item := range list {
list[k] = m.doFilterDataMapForInsertOrUpdate(item, false)
switch value := data.(type) {
case List:
for k, item := range value {
value[k] = m.doFilterDataMapForInsertOrUpdate(item, false)
}
return list
} else if item, ok := m.data.(Map); ok {
return m.doFilterDataMapForInsertOrUpdate(item, true)
return value
case Map:
return m.doFilterDataMapForInsertOrUpdate(value, true)
default:
return data
}
return data
}
// doFilterDataMapForInsertOrUpdate does the filter features for map.
@ -70,8 +75,13 @@ func (m *Model) doFilterDataMapForInsertOrUpdate(data Map, allowOmitEmpty bool)
if len(m.fields) > 0 && m.fields != "*" {
// Keep specified fields.
set := gset.NewStrSetFrom(gstr.SplitAndTrim(m.fields, ","))
var (
set = gset.NewStrSetFrom(gstr.SplitAndTrim(m.fields, ","))
charL, charR = m.db.GetChars()
chars = charL + charR
)
for k := range data {
k = gstr.Trim(k, chars)
if !set.Contains(k) {
delete(data, k)
}
@ -144,16 +154,17 @@ func (m *Model) checkAndRemoveCache() {
// Note that this function does not change any attribute value of the <m>.
//
// The parameter <limit> specifies whether limits querying only one record if m.limit is not set.
func (m *Model) formatCondition(limit bool) (condition string, conditionArgs []interface{}) {
var where string
func (m *Model) formatCondition(limit bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) {
if len(m.whereHolder) > 0 {
for _, v := range m.whereHolder {
switch v.operator {
case gWHERE_HOLDER_WHERE:
if where == "" {
newWhere, newArgs := formatWhere(m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0)
if conditionWhere == "" {
newWhere, newArgs := formatWhere(
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
)
if len(newWhere) > 0 {
where = newWhere
conditionWhere = newWhere
conditionArgs = newArgs
}
continue
@ -161,52 +172,69 @@ func (m *Model) formatCondition(limit bool) (condition string, conditionArgs []i
fallthrough
case gWHERE_HOLDER_AND:
newWhere, newArgs := formatWhere(m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0)
newWhere, newArgs := formatWhere(
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
)
if len(newWhere) > 0 {
if where[0] == '(' {
where = fmt.Sprintf(`%s AND (%s)`, where, newWhere)
if len(conditionWhere) == 0 {
conditionWhere = newWhere
} else if conditionWhere[0] == '(' {
conditionWhere = fmt.Sprintf(`%s AND (%s)`, conditionWhere, newWhere)
} else {
where = fmt.Sprintf(`(%s) AND (%s)`, where, newWhere)
conditionWhere = fmt.Sprintf(`(%s) AND (%s)`, conditionWhere, newWhere)
}
conditionArgs = append(conditionArgs, newArgs...)
}
case gWHERE_HOLDER_OR:
newWhere, newArgs := formatWhere(m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0)
newWhere, newArgs := formatWhere(
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
)
if len(newWhere) > 0 {
if where[0] == '(' {
where = fmt.Sprintf(`%s OR (%s)`, where, newWhere)
if len(conditionWhere) == 0 {
conditionWhere = newWhere
} else if conditionWhere[0] == '(' {
conditionWhere = fmt.Sprintf(`%s OR (%s)`, conditionWhere, newWhere)
} else {
where = fmt.Sprintf(`(%s) OR (%s)`, where, newWhere)
conditionWhere = fmt.Sprintf(`(%s) OR (%s)`, conditionWhere, newWhere)
}
conditionArgs = append(conditionArgs, newArgs...)
}
}
}
}
if where != "" {
condition += " WHERE " + where
if conditionWhere != "" {
conditionWhere = " WHERE " + conditionWhere
}
if m.groupBy != "" {
condition += " GROUP BY " + m.groupBy
conditionExtra += " GROUP BY " + m.groupBy
}
if m.orderBy != "" {
condition += " ORDER BY " + m.orderBy
conditionExtra += " ORDER BY " + m.orderBy
}
if len(m.having) > 0 {
havingStr, havingArgs := formatWhere(
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OPTION_OMITEMPTY > 0,
)
if len(havingStr) > 0 {
conditionExtra += " HAVING " + havingStr
conditionArgs = append(conditionArgs, havingArgs...)
}
}
if m.limit != 0 {
if m.start >= 0 {
condition += fmt.Sprintf(" LIMIT %d,%d", m.start, m.limit)
conditionExtra += fmt.Sprintf(" LIMIT %d,%d", m.start, m.limit)
} else {
condition += fmt.Sprintf(" LIMIT %d", m.limit)
conditionExtra += fmt.Sprintf(" LIMIT %d", m.limit)
}
} else if limit {
condition += " LIMIT 1"
conditionExtra += " LIMIT 1"
}
if m.offset >= 0 {
condition += fmt.Sprintf(" OFFSET %d", m.offset)
conditionExtra += fmt.Sprintf(" OFFSET %d", m.offset)
}
if m.lockInfo != "" {
condition += " " + m.lockInfo
conditionExtra += " " + m.lockInfo
}
return
}

View File

@ -40,6 +40,14 @@ func (s *Schema) Table(table string) *Model {
} else {
m = s.db.Table(table)
}
// Do not change the schema of the original db,
// it here creates a new db and changes its schema.
db, err := New(m.db.GetGroup())
if err != nil {
panic(err)
}
db.SetSchema(s.schema)
m.db = db
m.schema = s.schema
return m
}

View File

@ -1,117 +0,0 @@
// 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 gdb
import (
"github.com/gogf/gf/test/gtest"
"testing"
)
func Test_Func_FormatSqlWithArgs(t *testing.T) {
// mysql
gtest.C(t, func(t *gtest.T) {
var s string
s = FormatSqlWithArgs("select * from table where id>=? and sex=?", []interface{}{100, 1})
t.Assert(s, "select * from table where id>=100 and sex=1")
})
// mssql
gtest.C(t, func(t *gtest.T) {
var s string
s = FormatSqlWithArgs("select * from table where id>=@p1 and sex=@p2", []interface{}{100, 1})
t.Assert(s, "select * from table where id>=100 and sex=1")
})
// pgsql
gtest.C(t, func(t *gtest.T) {
var s string
s = FormatSqlWithArgs("select * from table where id>=$1 and sex=$2", []interface{}{100, 1})
t.Assert(s, "select * from table where id>=100 and sex=1")
})
// oracle
gtest.C(t, func(t *gtest.T) {
var s string
s = FormatSqlWithArgs("select * from table where id>=:1 and sex=:2", []interface{}{100, 1})
t.Assert(s, "select * from table where id>=100 and sex=1")
})
}
func Test_Func_doQuoteWord(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := map[string]string{
"user": "`user`",
"user u": "user u",
"user_detail": "`user_detail`",
"user,user_detail": "user,user_detail",
"user u, user_detail ut": "user u, user_detail ut",
"u.id asc": "u.id asc",
"u.id asc, ut.uid desc": "u.id asc, ut.uid desc",
}
for k, v := range array {
t.Assert(doQuoteWord(k, "`", "`"), v)
}
})
}
func Test_Func_doQuoteString(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
array := map[string]string{
"user": "`user`",
"user u": "`user` u",
"user,user_detail": "`user`,`user_detail`",
"user u, user_detail ut": "`user` u,`user_detail` ut",
"u.id asc": "`u`.`id` asc",
"u.id asc, ut.uid desc": "`u`.`id` asc,`ut`.`uid` desc",
"user.user u, user.user_detail ut": "`user`.`user` u,`user`.`user_detail` ut",
// mssql global schema access with double dots.
"user..user u, user.user_detail ut": "`user`..`user` u,`user`.`user_detail` ut",
}
for k, v := range array {
t.Assert(doQuoteString(k, "`", "`"), v)
}
})
}
func Test_Func_addTablePrefix(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
prefix := ""
array := map[string]string{
"user": "`user`",
"user u": "`user` u",
"user as u": "`user` as u",
"user,user_detail": "`user`,`user_detail`",
"user u, user_detail ut": "`user` u,`user_detail` ut",
"`user`.user_detail": "`user`.`user_detail`",
"`user`.`user_detail`": "`user`.`user_detail`",
"user as u, user_detail as ut": "`user` as u,`user_detail` as ut",
"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`user` as u,`UserCenter`.`user_detail` as ut",
// mssql global schema access with double dots.
"UserCenter..user as u, user_detail as ut": "`UserCenter`..`user` as u,`user_detail` as ut",
}
for k, v := range array {
t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
}
})
gtest.C(t, func(t *gtest.T) {
prefix := "gf_"
array := map[string]string{
"user": "`gf_user`",
"user u": "`gf_user` u",
"user as u": "`gf_user` as u",
"user,user_detail": "`gf_user`,`gf_user_detail`",
"user u, user_detail ut": "`gf_user` u,`gf_user_detail` ut",
"`user`.user_detail": "`user`.`gf_user_detail`",
"`user`.`user_detail`": "`user`.`gf_user_detail`",
"user as u, user_detail as ut": "`gf_user` as u,`gf_user_detail` as ut",
"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`gf_user` as u,`UserCenter`.`gf_user_detail` as ut",
// mssql global schema access with double dots.
"UserCenter..user as u, user_detail as ut": "`UserCenter`..`gf_user` as u,`gf_user_detail` as ut",
}
for k, v := range array {
t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
}
})
}

View File

@ -0,0 +1,289 @@
// 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 gdb
import (
"fmt"
"github.com/gogf/gf/os/gcmd"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/test/gtest"
"testing"
)
const (
SCHEMA = "test_internal"
)
var (
db DB
configNode ConfigNode
)
func init() {
parser, err := gcmd.Parse(map[string]bool{
"name": true,
"type": true,
}, false)
gtest.Assert(err, nil)
configNode = ConfigNode{
Host: "127.0.0.1",
Port: "3306",
User: "root",
Pass: "12345678",
Name: parser.GetOpt("name", ""),
Type: parser.GetOpt("type", "mysql"),
Role: "master",
Charset: "utf8",
Weight: 1,
MaxIdleConnCount: 10,
MaxOpenConnCount: 10,
MaxConnLifetime: 600,
}
AddConfigNode(DEFAULT_GROUP_NAME, configNode)
// Default db.
if r, err := New(); err != nil {
gtest.Error(err)
} else {
db = r
}
schemaTemplate := "CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET UTF8"
if _, err := db.Exec(fmt.Sprintf(schemaTemplate, SCHEMA)); err != nil {
gtest.Error(err)
}
db.SetSchema(SCHEMA)
}
func dropTable(table string) {
if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
gtest.Error(err)
}
}
func Test_Func_FormatSqlWithArgs(t *testing.T) {
// mysql
gtest.C(t, func(t *gtest.T) {
var s string
s = FormatSqlWithArgs("select * from table where id>=? and sex=?", []interface{}{100, 1})
t.Assert(s, "select * from table where id>=100 and sex=1")
})
// mssql
gtest.C(t, func(t *gtest.T) {
var s string
s = FormatSqlWithArgs("select * from table where id>=@p1 and sex=@p2", []interface{}{100, 1})
t.Assert(s, "select * from table where id>=100 and sex=1")
})
// pgsql
gtest.C(t, func(t *gtest.T) {
var s string
s = FormatSqlWithArgs("select * from table where id>=$1 and sex=$2", []interface{}{100, 1})
t.Assert(s, "select * from table where id>=100 and sex=1")
})
// oracle
gtest.C(t, func(t *gtest.T) {
var s string
s = FormatSqlWithArgs("select * from table where id>=:1 and sex=:2", []interface{}{100, 1})
t.Assert(s, "select * from table where id>=100 and sex=1")
})
}
func Test_Func_doQuoteWord(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := map[string]string{
"user": "`user`",
"user u": "user u",
"user_detail": "`user_detail`",
"user,user_detail": "user,user_detail",
"user u, user_detail ut": "user u, user_detail ut",
"u.id asc": "u.id asc",
"u.id asc, ut.uid desc": "u.id asc, ut.uid desc",
}
for k, v := range array {
t.Assert(doQuoteWord(k, "`", "`"), v)
}
})
}
func Test_Func_doQuoteString(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
array := map[string]string{
"user": "`user`",
"user u": "`user` u",
"user,user_detail": "`user`,`user_detail`",
"user u, user_detail ut": "`user` u,`user_detail` ut",
"u.id, u.name, u.age": "`u`.`id`,`u`.`name`,`u`.`age`",
"u.id asc": "`u`.`id` asc",
"u.id asc, ut.uid desc": "`u`.`id` asc,`ut`.`uid` desc",
"user.user u, user.user_detail ut": "`user`.`user` u,`user`.`user_detail` ut",
// mssql global schema access with double dots.
"user..user u, user.user_detail ut": "`user`..`user` u,`user`.`user_detail` ut",
}
for k, v := range array {
t.Assert(doQuoteString(k, "`", "`"), v)
}
})
}
func Test_Func_addTablePrefix(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
prefix := ""
array := map[string]string{
"user": "`user`",
"user u": "`user` u",
"user as u": "`user` as u",
"user,user_detail": "`user`,`user_detail`",
"user u, user_detail ut": "`user` u,`user_detail` ut",
"`user`.user_detail": "`user`.`user_detail`",
"`user`.`user_detail`": "`user`.`user_detail`",
"user as u, user_detail as ut": "`user` as u,`user_detail` as ut",
"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`user` as u,`UserCenter`.`user_detail` as ut",
// mssql global schema access with double dots.
"UserCenter..user as u, user_detail as ut": "`UserCenter`..`user` as u,`user_detail` as ut",
}
for k, v := range array {
t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
}
})
gtest.C(t, func(t *gtest.T) {
prefix := "gf_"
array := map[string]string{
"user": "`gf_user`",
"user u": "`gf_user` u",
"user as u": "`gf_user` as u",
"user,user_detail": "`gf_user`,`gf_user_detail`",
"user u, user_detail ut": "`gf_user` u,`gf_user_detail` ut",
"`user`.user_detail": "`user`.`gf_user_detail`",
"`user`.`user_detail`": "`user`.`gf_user_detail`",
"user as u, user_detail as ut": "`gf_user` as u,`gf_user_detail` as ut",
"UserCenter.user as u, UserCenter.user_detail as ut": "`UserCenter`.`gf_user` as u,`UserCenter`.`gf_user_detail` as ut",
// mssql global schema access with double dots.
"UserCenter..user as u, user_detail as ut": "`UserCenter`..`gf_user` as u,`gf_user_detail` as ut",
}
for k, v := range array {
t.Assert(doHandleTableName(k, prefix, "`", "`"), v)
}
})
}
func Test_Model_getSoftFieldName(t *testing.T) {
table1 := "soft_deleting_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
create_at datetime DEFAULT NULL,
update_at datetime DEFAULT NULL,
delete_at datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table1)); err != nil {
gtest.Error(err)
}
defer dropTable(table1)
table2 := "soft_deleting_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
createat datetime DEFAULT NULL,
updateat datetime DEFAULT NULL,
deleteat datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table2)); err != nil {
gtest.Error(err)
}
defer dropTable(table2)
gtest.C(t, func(t *gtest.T) {
model := db.Table(table1)
gtest.Assert(model.getSoftFieldNameCreate(table2), "createat")
gtest.Assert(model.getSoftFieldNameUpdate(table2), "updateat")
gtest.Assert(model.getSoftFieldNameDelete(table2), "deleteat")
})
}
func Test_Model_getConditionForSoftDeleting(t *testing.T) {
table1 := "soft_deleting_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE %s (
id1 int(11) NOT NULL,
name1 varchar(45) DEFAULT NULL,
create_at datetime DEFAULT NULL,
update_at datetime DEFAULT NULL,
delete_at datetime DEFAULT NULL,
PRIMARY KEY (id1)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table1)); err != nil {
gtest.Error(err)
}
defer dropTable(table1)
table2 := "soft_deleting_table_" + gtime.TimestampNanoStr()
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE %s (
id2 int(11) NOT NULL,
name2 varchar(45) DEFAULT NULL,
createat datetime DEFAULT NULL,
updateat datetime DEFAULT NULL,
deleteat datetime DEFAULT NULL,
PRIMARY KEY (id2)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table2)); err != nil {
gtest.Error(err)
}
defer dropTable(table2)
gtest.C(t, func(t *gtest.T) {
model := db.Table(table1)
t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL")
})
gtest.C(t, func(t *gtest.T) {
model := db.Table(fmt.Sprintf(`%s as t`, table1))
t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL")
})
gtest.C(t, func(t *gtest.T) {
model := db.Table(fmt.Sprintf(`%s, %s`, table1, table2))
t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL",
table1, table2,
))
})
gtest.C(t, func(t *gtest.T) {
model := db.Table(fmt.Sprintf(`%s t1, %s as t2`, table1, table2))
t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
})
gtest.C(t, func(t *gtest.T) {
model := db.Table(fmt.Sprintf(`%s as t1, %s as t2`, table1, table2))
t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
})
gtest.C(t, func(t *gtest.T) {
model := db.Table(fmt.Sprintf(`%s as t1`, table1)).LeftJoin(table2+" t2", "t2.id2=t1.id1")
t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
})
gtest.C(t, func(t *gtest.T) {
model := db.Table(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1")
t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL",
table1, table2,
))
})
gtest.C(t, func(t *gtest.T) {
model := db.Table(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1").RightJoin(table2, "t2.id2=t1.id1")
t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL AND `%s`.`deleteat` IS NULL",
table1, table2, table2,
))
})
gtest.C(t, func(t *gtest.T) {
model := db.Table(table1+" as t1").LeftJoin(table2+" as t2", "t2.id2=t1.id1").RightJoin(table2+" as t3 ", "t2.id2=t1.id1")
t.Assert(
model.getConditionForSoftDeleting(),
"`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL AND `t3`.`deleteat` IS NULL",
)
})
}

View File

@ -26,7 +26,7 @@ func Test_Model_Insert(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
user := db.From(table)
user := db.Table(table)
result, err := user.Filter().Data(g.Map{
"id": 1,
"uid": 1,
@ -338,7 +338,7 @@ func Test_Model_Safe(t *testing.T) {
t.Assert(err, nil)
t.Assert(len(all), 2)
all, err = md2.ForPage(1, 10).All()
all, err = md2.Page(1, 10).All()
t.Assert(err, nil)
t.Assert(len(all), 2)
})
@ -362,7 +362,7 @@ func Test_Model_Safe(t *testing.T) {
t.Assert(all[0]["id"].Int(), 1)
t.Assert(all[1]["id"].Int(), 3)
all, err = md2.ForPage(1, 10).All()
all, err = md2.Page(1, 10).All()
t.Assert(err, nil)
t.Assert(len(all), 2)
@ -378,7 +378,7 @@ func Test_Model_Safe(t *testing.T) {
t.Assert(all[1]["id"].Int(), 5)
t.Assert(all[2]["id"].Int(), 6)
all, err = md3.ForPage(1, 10).All()
all, err = md3.Page(1, 10).All()
t.Assert(err, nil)
t.Assert(len(all), 3)
})
@ -1274,6 +1274,11 @@ func Test_Model_Where_GTime(t *testing.T) {
t.Assert(err, nil)
t.Assert(len(result), 10)
})
gtest.C(t, func(t *gtest.T) {
result, err := db.Table(table).Where("create_time>?", *gtime.NewFromStr("2010-09-01")).All()
t.Assert(err, nil)
t.Assert(len(result), 10)
})
}
func Test_Model_WherePri(t *testing.T) {
@ -1566,7 +1571,7 @@ func Test_Model_Offset(t *testing.T) {
})
}
func Test_Model_ForPage(t *testing.T) {
func Test_Model_Page(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
@ -1576,6 +1581,15 @@ func Test_Model_ForPage(t *testing.T) {
t.Assert(result[0]["id"], 7)
t.Assert(result[1]["id"], 8)
})
gtest.C(t, func(t *gtest.T) {
model := db.Table(table).Safe().Order("id")
all, err := model.Page(3, 3).All()
count, err := model.Count()
t.Assert(err, nil)
t.Assert(len(all), 3)
t.Assert(all[0]["id"], "7")
t.Assert(count, SIZE)
})
}
func Test_Model_Option_Map(t *testing.T) {
@ -2158,3 +2172,71 @@ func Test_Model_DryRun(t *testing.T) {
t.Assert(n, 0)
})
}
func Test_Model_Cache(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Cache(time.Second, "test1").FindOne(1)
t.Assert(err, nil)
t.Assert(one["passport"], "user_1")
r, err := db.Table(table).Data("passport", "user_100").WherePri(1).Update()
t.Assert(err, nil)
n, err := r.RowsAffected()
t.Assert(err, nil)
t.Assert(n, 1)
one, err = db.Table(table).Cache(time.Second, "test1").FindOne(1)
t.Assert(err, nil)
t.Assert(one["passport"], "user_1")
time.Sleep(time.Second * 2)
one, err = db.Table(table).Cache(time.Second, "test1").FindOne(1)
t.Assert(err, nil)
t.Assert(one["passport"], "user_100")
})
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Cache(time.Second, "test2").FindOne(2)
t.Assert(err, nil)
t.Assert(one["passport"], "user_2")
r, err := db.Table(table).Data("passport", "user_200").Cache(-1, "test2").WherePri(2).Update()
t.Assert(err, nil)
n, err := r.RowsAffected()
t.Assert(err, nil)
t.Assert(n, 1)
one, err = db.Table(table).Cache(time.Second, "test2").FindOne(2)
t.Assert(err, nil)
t.Assert(one["passport"], "user_200")
})
}
func Test_Model_Having(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id > 1").Having("id > 8").All()
t.Assert(err, nil)
t.Assert(len(all), 2)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id > 1").Having("id > ?", 8).All()
t.Assert(err, nil)
t.Assert(len(all), 2)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id > ?", 1).Having("id > ?", 8).All()
t.Assert(err, nil)
t.Assert(len(all), 2)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id > ?", 1).Having("id", 8).All()
t.Assert(err, nil)
t.Assert(len(all), 1)
})
}

View File

@ -0,0 +1,370 @@
// 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 gdb_test
import (
"fmt"
"github.com/gogf/gf/os/gtime"
"testing"
"time"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/test/gtest"
)
func Test_CreateUpdateDeleteTime(t *testing.T) {
table := "time_test_table"
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
create_at datetime DEFAULT NULL,
update_at datetime DEFAULT NULL,
delete_at datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table)); err != nil {
gtest.Error(err)
}
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
// Insert
dataInsert := g.Map{
"id": 1,
"name": "name_1",
}
r, err := db.Table(table).Data(dataInsert).Insert()
t.Assert(err, nil)
n, _ := r.RowsAffected()
t.Assert(n, 1)
oneInsert, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(oneInsert["id"].Int(), 1)
t.Assert(oneInsert["name"].String(), "name_1")
t.Assert(oneInsert["delete_at"].String(), "")
t.AssertGE(oneInsert["create_at"].GTime().Timestamp(), gtime.Timestamp()-2)
t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp())
time.Sleep(2 * time.Second)
// Save
dataSave := g.Map{
"id": 1,
"name": "name_10",
}
r, err = db.Table(table).Data(dataSave).Save()
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 2)
oneSave, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(oneSave["id"].Int(), 1)
t.Assert(oneSave["name"].String(), "name_10")
t.Assert(oneSave["delete_at"].String(), "")
t.Assert(oneSave["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp())
t.AssertNE(oneSave["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp())
t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2)
time.Sleep(2 * time.Second)
// Update
dataUpdate := g.Map{
"id": 1,
"name": "name_1000",
}
r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update()
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 1)
oneUpdate, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(oneUpdate["id"].Int(), 1)
t.Assert(oneUpdate["name"].String(), "name_1000")
t.Assert(oneUpdate["delete_at"].String(), "")
t.Assert(oneUpdate["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp())
t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2)
// Replace
dataReplace := g.Map{
"id": 1,
"name": "name_100",
}
r, err = db.Table(table).Data(dataReplace).Replace()
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 2)
oneReplace, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(oneReplace["id"].Int(), 1)
t.Assert(oneReplace["name"].String(), "name_100")
t.Assert(oneReplace["delete_at"].String(), "")
t.AssertGE(oneReplace["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp())
t.AssertGE(oneReplace["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp())
time.Sleep(2 * time.Second)
// Delete
r, err = db.Table(table).Delete("id", 1)
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 1)
// Delete Select
one4, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(len(one4), 0)
one5, err := db.Table(table).Unscoped().FindOne(1)
t.Assert(err, nil)
t.Assert(one5["id"].Int(), 1)
t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2)
// Delete Count
i, err := db.Table(table).FindCount()
t.Assert(err, nil)
t.Assert(i, 0)
i, err = db.Table(table).Unscoped().FindCount()
t.Assert(err, nil)
t.Assert(i, 1)
// Delete Unscoped
r, err = db.Table(table).Unscoped().Delete("id", 1)
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 1)
one6, err := db.Table(table).Unscoped().FindOne(1)
t.Assert(err, nil)
t.Assert(len(one6), 0)
i, err = db.Table(table).Unscoped().FindCount()
t.Assert(err, nil)
t.Assert(i, 0)
})
}
func Test_SoftDelete_Join(t *testing.T) {
table1 := "time_test_table1"
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
create_at datetime DEFAULT NULL,
update_at datetime DEFAULT NULL,
delete_at datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table1)); err != nil {
gtest.Error(err)
}
defer dropTable(table1)
table2 := "time_test_table2"
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
createat datetime DEFAULT NULL,
updateat datetime DEFAULT NULL,
deleteat datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table2)); err != nil {
gtest.Error(err)
}
defer dropTable(table2)
gtest.C(t, func(t *gtest.T) {
//db.SetDebug(true)
dataInsert1 := g.Map{
"id": 1,
"name": "name_1",
}
r, err := db.Table(table1).Data(dataInsert1).Insert()
t.Assert(err, nil)
n, _ := r.RowsAffected()
t.Assert(n, 1)
dataInsert2 := g.Map{
"id": 1,
"name": "name_2",
}
r, err = db.Table(table2).Data(dataInsert2).Insert()
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 1)
one, err := db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
t.Assert(err, nil)
t.Assert(one["name"], "name_1")
// Soft deleting.
r, err = db.Table(table1).Delete()
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 1)
one, err = db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
t.Assert(err, nil)
t.Assert(one.IsEmpty(), true)
one, err = db.Table(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").FindOne()
t.Assert(err, nil)
t.Assert(one.IsEmpty(), true)
})
}
func Test_CreateUpdateTime_Struct(t *testing.T) {
table := "time_test_table"
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE %s (
id int(11) NOT NULL,
name varchar(45) DEFAULT NULL,
create_at datetime DEFAULT NULL,
update_at datetime DEFAULT NULL,
delete_at datetime DEFAULT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table)); err != nil {
gtest.Error(err)
}
defer dropTable(table)
type Entity struct {
Id uint64 `orm:"id,primary" json:"id"`
Name string `orm:"name" json:"name"`
CreateAt *gtime.Time `orm:"create_at" json:"create_at"`
UpdateAt *gtime.Time `orm:"update_at" json:"update_at"`
DeleteAt *gtime.Time `orm:"delete_at" json:"delete_at"`
}
gtest.C(t, func(t *gtest.T) {
// Insert
dataInsert := &Entity{
Id: 1,
Name: "name_1",
CreateAt: nil,
UpdateAt: nil,
DeleteAt: nil,
}
r, err := db.Table(table).Data(dataInsert).Insert()
t.Assert(err, nil)
n, _ := r.RowsAffected()
t.Assert(n, 1)
oneInsert, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(oneInsert["id"].Int(), 1)
t.Assert(oneInsert["name"].String(), "name_1")
t.Assert(oneInsert["delete_at"].String(), "")
t.AssertGE(oneInsert["create_at"].GTime().Timestamp(), gtime.Timestamp()-2)
t.AssertGE(oneInsert["update_at"].GTime().Timestamp(), gtime.Timestamp())
time.Sleep(2 * time.Second)
// Save
dataSave := &Entity{
Id: 1,
Name: "name_10",
CreateAt: nil,
UpdateAt: nil,
DeleteAt: nil,
}
r, err = db.Table(table).Data(dataSave).Save()
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 2)
oneSave, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(oneSave["id"].Int(), 1)
t.Assert(oneSave["name"].String(), "name_10")
t.Assert(oneSave["delete_at"].String(), "")
t.Assert(oneSave["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp())
t.AssertNE(oneSave["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp())
t.AssertGE(oneSave["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2)
time.Sleep(2 * time.Second)
// Update
dataUpdate := &Entity{
Id: 1,
Name: "name_1000",
CreateAt: nil,
UpdateAt: nil,
DeleteAt: nil,
}
r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update()
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 1)
oneUpdate, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(oneUpdate["id"].Int(), 1)
t.Assert(oneUpdate["name"].String(), "name_1000")
t.Assert(oneUpdate["delete_at"].String(), "")
t.Assert(oneUpdate["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp())
t.AssertGE(oneUpdate["update_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2)
// Replace
dataReplace := &Entity{
Id: 1,
Name: "name_100",
CreateAt: nil,
UpdateAt: nil,
DeleteAt: nil,
}
r, err = db.Table(table).Data(dataReplace).Replace()
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 2)
oneReplace, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(oneReplace["id"].Int(), 1)
t.Assert(oneReplace["name"].String(), "name_100")
t.Assert(oneReplace["delete_at"].String(), "")
t.AssertGE(oneReplace["create_at"].GTime().Timestamp(), oneInsert["create_at"].GTime().Timestamp())
t.AssertGE(oneReplace["update_at"].GTime().Timestamp(), oneInsert["update_at"].GTime().Timestamp())
time.Sleep(2 * time.Second)
// Delete
r, err = db.Table(table).Delete("id", 1)
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 1)
// Delete Select
one4, err := db.Table(table).FindOne(1)
t.Assert(err, nil)
t.Assert(len(one4), 0)
one5, err := db.Table(table).Unscoped().FindOne(1)
t.Assert(err, nil)
t.Assert(one5["id"].Int(), 1)
t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Now().Timestamp()-2)
// Delete Count
i, err := db.Table(table).FindCount()
t.Assert(err, nil)
t.Assert(i, 0)
i, err = db.Table(table).Unscoped().FindCount()
t.Assert(err, nil)
t.Assert(i, 1)
// Delete Unscoped
r, err = db.Table(table).Unscoped().Delete("id", 1)
t.Assert(err, nil)
n, _ = r.RowsAffected()
t.Assert(n, 1)
one6, err := db.Table(table).Unscoped().FindOne(1)
t.Assert(err, nil)
t.Assert(len(one6), 0)
i, err = db.Table(table).Unscoped().FindCount()
t.Assert(err, nil)
t.Assert(i, 0)
})
}

View File

@ -120,7 +120,7 @@ func Test_TX_Insert(t *testing.T) {
if err != nil {
gtest.Error(err)
}
user := tx.From(table)
user := tx.Table(table)
if _, err := user.Data(g.Map{
"id": 1,
"passport": "t1",

View File

@ -42,12 +42,14 @@ func init() {
}
}
// CallerPath returns the function name and the absolute file path along with its line number of the caller.
// CallerPath returns the function name and the absolute file path along with its line
// number of the caller.
func Caller(skip ...int) (function string, path string, line int) {
return CallerWithFilter("", skip...)
}
// CallerPathWithFilter returns the function name and the absolute file path along with its line number of the caller.
// CallerPathWithFilter returns the function name and the absolute file path along with
// its line number of the caller.
//
// The parameter <filter> is used to filter the path of the caller.
func CallerWithFilter(filter string, skip ...int) (function string, path string, line int) {
@ -84,7 +86,10 @@ func CallerWithFilter(filter string, skip ...int) (function string, path string,
return "", "", -1
}
// callerFromIndex returns the caller position and according information exclusive of the debug package.
// callerFromIndex returns the caller position and according information exclusive of the
// debug package.
//
// VERY NOTE THAT, the returned index value should be <index - 1> as the caller's start point.
func callerFromIndex(filters []string) (pc uintptr, file string, line int, index int) {
var filtered, ok bool
for index = 0; index < gMAX_DEPTH; index++ {
@ -102,6 +107,9 @@ func callerFromIndex(filters []string) (pc uintptr, file string, line int, index
if strings.Contains(file, gFILTER_KEY) {
continue
}
if index > 0 {
index--
}
return
}
}

View File

@ -35,25 +35,33 @@ func StackWithFilter(filter string, skip ...int) string {
// StackWithFilters returns a formatted stack trace of the goroutine that calls it.
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
//
// The parameter <filters> is a slice of strings, which are used to filter the path of the caller.
// The parameter <filters> is a slice of strings, which are used to filter the path of the
// caller.
//
// TODO Improve the performance using debug.Stack.
func StackWithFilters(filters []string, skip ...int) string {
number := 0
if len(skip) > 0 {
number = skip[0]
}
name := ""
space := " "
index := 1
buffer := bytes.NewBuffer(nil)
filtered := false
ok := true
pc, file, line, start := callerFromIndex(filters)
var (
name = ""
space = " "
index = 1
buffer = bytes.NewBuffer(nil)
filtered = false
ok = true
pc, file, line, start = callerFromIndex(filters)
)
for i := start + number; i < gMAX_DEPTH; i++ {
if i != start {
pc, file, line, ok = runtime.Caller(i)
}
if ok {
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
// GOROOT filter.
if goRootForFilter != "" &&
len(file) >= len(goRootForFilter) &&
file[0:len(goRootForFilter)] == goRootForFilter {
continue
}
filtered = false

View File

@ -10,6 +10,7 @@ package gdebug
import (
"runtime"
"runtime/debug"
"testing"
)
@ -49,6 +50,12 @@ func Benchmark_Stack(b *testing.B) {
}
}
func Benchmark_StackOfStdlib(b *testing.B) {
for i := 0; i < b.N; i++ {
debug.Stack()
}
}
func Benchmark_StackWithFilter(b *testing.B) {
for i := 0; i < b.N; i++ {
StackWithFilter("test")

View File

@ -74,7 +74,7 @@ func NewWithTag(data interface{}, tags string, safe ...bool) *Json {
i := interface{}(nil)
// Note that it uses Map function implementing the converting.
// Note that it here should not use MapDeep function if you really know what it means.
i = gconv.Map(data, tags)
i = gconv.MapDeep(data, tags)
j = &Json{
p: &i,
c: byte(gDEFAULT_SPLIT_CHAR),

View File

@ -12,12 +12,17 @@ import (
"strings"
)
// url encode string, is + not %20
// Encode escapes the string so it can be safely placed
// inside a URL query.
func Encode(str string) string {
return url.QueryEscape(str)
}
// url decode string
// Decode does the inverse transformation of Encode,
// converting each 3-byte encoded substring of the form "%AB" into the
// hex-decoded byte 0xAB.
// It returns an error if any % is not followed by two hexadecimal
// digits.
func Decode(str string) (string, error) {
return url.QueryUnescape(str)
}

View File

@ -91,6 +91,13 @@ func DB(name ...string) gdb.DB {
return gins.Database(name...)
}
// Table creates and returns a model from specified database or default database configuration.
// The optional parameter <db> specifies the configuration group name of the database,
// which is "default" in default.
func Table(tables string, db ...string) *gdb.Model {
return DB(db...).Table(tables)
}
// Redis returns an instance of redis client with specified configuration group name.
func Redis(name ...string) *gredis.Redis {
return gins.Redis(name...)

View File

@ -99,7 +99,7 @@ func (view *View) BindFuncMap(funcMap gview.FuncMap) {
// Display parses and writes the parsed template file content to http response.
func (view *View) Display(file ...string) error {
name := "index.tpl"
name := view.view.GetDefaultFile()
if len(file) > 0 {
name = file[0]
}

2
go.mod
View File

@ -1,6 +1,6 @@
module github.com/gogf/gf
go 1.13
go 1.11
require (
github.com/BurntSushi/toml v0.3.1

View File

@ -12,8 +12,8 @@ import (
)
// IsEmpty checks whether given <value> empty.
// It returns true if <value> is in: 0, nil, false, "", len(slice/map/chan) == 0.
// Or else it returns true.
// It returns true if <value> is in: 0, nil, false, "", len(slice/map/chan) == 0,
// or else it returns false.
func IsEmpty(value interface{}) bool {
if value == nil {
return true
@ -49,6 +49,8 @@ func IsEmpty(value interface{}) bool {
return value == ""
case []byte:
return len(value) == 0
case []rune:
return len(value) == 0
default:
// Finally using reflect.
var rv reflect.Value

View File

@ -20,7 +20,15 @@ type RWMutex struct {
// The parameter <safe> is used to specify whether using this mutex in concurrent safety,
// which is false in default.
func New(safe ...bool) *RWMutex {
mu := new(RWMutex)
mu := Create(safe...)
return &mu
}
// Create creates and returns a new RWMutex object.
// The parameter <safe> is used to specify whether using this mutex in concurrent safety,
// which is false in default.
func Create(safe ...bool) RWMutex {
mu := RWMutex{}
if len(safe) > 0 && safe[0] {
mu.RWMutex = new(sync.RWMutex)
}

View File

@ -14,6 +14,8 @@ import (
// MapField retrieves struct field as map[name/tag]*Field from <pointer>, and returns the map.
//
// The parameter <pointer> should be type of struct/*struct.
//
// The parameter <priority> specifies the priority tag array for retrieving from high to low.
//
// The parameter <recursive> specifies whether retrieving the struct field recursively.

View File

@ -14,6 +14,8 @@ import (
// TagFields retrieves struct tags as []*Field from <pointer>, and returns it.
//
// The parameter <pointer> should be type of struct/*struct.
//
// The parameter <recursive> specifies whether retrieving the struct field recursively.
//
// Note that it only retrieves the exported attributes with first letter up-case from struct.
@ -23,6 +25,7 @@ func TagFields(pointer interface{}, priority []string, recursive bool) []*Field
// doTagFields retrieves the tag and corresponding attribute name from <pointer>. It also filters repeated
// tag internally.
// The parameter <pointer> should be type of struct/*struct.
func doTagFields(pointer interface{}, priority []string, recursive bool, tagMap map[string]struct{}) []*Field {
var fields []*structs.Field
if v, ok := pointer.(reflect.Value); ok {
@ -85,6 +88,8 @@ func doTagFields(pointer interface{}, priority []string, recursive bool, tagMap
// TagMapName retrieves struct tags as map[tag]attribute from <pointer>, and returns it.
//
// The parameter <pointer> should be type of struct/*struct.
//
// The parameter <recursive> specifies whether retrieving the struct field recursively.
//
// Note that it only retrieves the exported attributes with first letter up-case from struct.
@ -99,6 +104,8 @@ func TagMapName(pointer interface{}, priority []string, recursive bool) map[stri
// TagMapField retrieves struct tags as map[tag]*Field from <pointer>, and returns it.
//
// The parameter <pointer> should be type of struct/*struct.
//
// The parameter <recursive> specifies whether retrieving the struct field recursively.
//
// Note that it only retrieves the exported attributes with first letter up-case from struct.

View File

@ -6,7 +6,16 @@
package utils
import "strings"
import (
"regexp"
"strings"
)
var (
// replaceCharReg is the regular expression object for replacing chars in key.
// It is used for function EqualFoldWithoutChars.
replaceCharReg, _ = regexp.Compile(`[\-\.\_\s]+`)
)
// IsLetterUpper checks whether the given byte b is in upper case.
func IsLetterUpper(b byte) bool {
@ -73,3 +82,12 @@ func ReplaceByMap(origin string, replaces map[string]string) string {
}
return origin
}
// EqualFoldWithoutChars checks string <s1> and <s2> equal case-insensitively,
// with/without chars '-'/'_'/'.'/' '.
func EqualFoldWithoutChars(s1, s2 string) bool {
return strings.EqualFold(
replaceCharReg.ReplaceAllString(s1, ""),
replaceCharReg.ReplaceAllString(s2, ""),
)
}

View File

@ -11,6 +11,17 @@ import (
"time"
)
// Prefix is a chaining function,
// which sets the URL prefix for next request of this client.
func (c *Client) Prefix(prefix string) *Client {
newClient := c
if c.parent == nil {
newClient = c.Clone()
}
newClient.SetPrefix(prefix)
return newClient
}
// Header is a chaining function,
// which sets custom HTTP headers with map for next request.
func (c *Client) Header(m map[string]string) *Client {
@ -113,3 +124,14 @@ func (c *Client) Ctx(ctx context.Context) *Client {
newClient.SetCtx(ctx)
return newClient
}
// Retry is a chaining function,
// which sets retry count and interval when failure for next request.
func (c *Client) Retry(retryCount int, retryInterval time.Duration) *Client {
newClient := c
if c.parent == nil {
newClient = c.Clone()
}
newClient.SetRetry(retryCount, retryInterval)
return c
}

View File

@ -28,7 +28,7 @@ type Client struct {
authPass string // HTTP basic authentication: pass.
browserMode bool // Whether auto saving and sending cookie content.
retryCount int // Retry count when request fails.
retryInterval int // Retry interval when request fails.
retryInterval time.Duration // Retry interval when request fails.
}
// NewClient creates and returns a new HTTP client object.
@ -43,7 +43,6 @@ func NewClient() *Client {
DisableKeepAlives: true,
},
},
ctx: context.Background(),
header: make(map[string]string),
cookies: make(map[string]string),
}
@ -143,7 +142,7 @@ func (c *Client) SetCtx(ctx context.Context) *Client {
}
// SetRetry sets retry count and interval.
func (c *Client) SetRetry(retryCount int, retryInterval int) *Client {
func (c *Client) SetRetry(retryCount int, retryInterval time.Duration) *Client {
c.retryCount = retryCount
c.retryInterval = retryInterval
return c

View File

@ -157,7 +157,8 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
if err = writer.Close(); err != nil {
return nil, err
}
if req, err = http.NewRequestWithContext(c.ctx, method, url, buffer); err != nil {
if req, err = http.NewRequest(method, url, buffer); err != nil {
return nil, err
} else {
req.Header.Set("Content-Type", writer.FormDataContentType())
@ -165,9 +166,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
} else {
// Normal request.
paramBytes := []byte(param)
if req, err = http.NewRequestWithContext(
c.ctx, method, url, bytes.NewReader(paramBytes),
); err != nil {
if req, err = http.NewRequest(method, url, bytes.NewReader(paramBytes)); err != nil {
return nil, err
} else {
if v, ok := c.header["Content-Type"]; ok {
@ -184,6 +183,10 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
}
}
}
// Context.
if c.ctx != nil {
req = req.WithContext(c.ctx)
}
// Custom header.
if len(c.header) > 0 {
for k, v := range c.header {
@ -191,7 +194,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
}
}
// It's necessary set the req.Host if you want to custom the host value of the request.
// It uses the "Host" value of the header.
// It uses the "Host" value from header if it's not set in the request.
if host := req.Header.Get("Host"); host != "" && req.Host == "" {
req.Host = host
}
@ -220,6 +223,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien
if r, err = c.Do(req); err != nil {
if c.retryCount > 0 {
c.retryCount--
time.Sleep(c.retryInterval)
} else {
// we need a copy of the request when the request fails.
resp.req = req

View File

@ -24,12 +24,8 @@ type ClientResponse struct {
// initCookie initializes the cookie map attribute of ClientResponse.
func (r *ClientResponse) initCookie() {
if r.cookies == nil {
now := time.Now()
r.cookies = make(map[string]string)
for _, v := range r.Cookies() {
if v.Expires.UnixNano() < now.UnixNano() {
continue
}
r.cookies[v.Name] = v.Value
}
}

View File

@ -19,9 +19,15 @@ import (
// The optional parameter <noUrlEncode> specifies whether ignore the url encoding for the data.
func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr string) {
// If given string/[]byte, converts and returns it directly as string.
switch params.(type) {
switch v := params.(type) {
case string, []byte:
return gconv.String(params)
case []interface{}:
if len(v) > 0 {
params = v[0]
} else {
params = nil
}
}
// Else converts it to map and does the url encoding.
m, urlEncode := gconv.Map(params), true

View File

@ -14,18 +14,6 @@ import (
"github.com/gogf/gf/encoding/gbase64"
)
// setBasicAuth sets the http basic authentication tips.
func (r *Request) setBasicAuth(tips ...string) {
realm := ""
if len(tips) > 0 && tips[0] != "" {
realm = tips[0]
} else {
realm = "Need Login"
}
r.Response.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
r.Response.WriteHeader(http.StatusUnauthorized)
}
// BasicAuth enables the http basic authentication feature with given passport and password
// and asks client for authentication. It returns true if authentication success, else returns
// false if failure.
@ -62,5 +50,16 @@ func (r *Request) BasicAuth(user, pass string, tips ...string) bool {
r.Response.WriteStatus(http.StatusForbidden)
return false
}
return false
}
// setBasicAuth sets the http basic authentication tips.
func (r *Request) setBasicAuth(tips ...string) {
realm := ""
if len(tips) > 0 && tips[0] != "" {
realm = tips[0]
} else {
realm = "Need Login"
}
r.Response.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm="%s"`, realm))
r.Response.WriteHeader(http.StatusUnauthorized)
}

View File

@ -1,23 +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 this file,
// You can obtain one at https://github.com/gogf/gf.
package ghttp
// 获得当前请求,指定类型的的钩子函数列表
func (r *Request) getHookHandlers(hook string) []*handlerParsedItem {
if !r.hasHookHandler {
return nil
}
parsedItems := make([]*handlerParsedItem, 0, 4)
for _, v := range r.handlers {
if v.handler.hookName != hook {
continue
}
item := v
parsedItems = append(parsedItems, item)
}
return parsedItems
}

View File

@ -24,6 +24,7 @@ type Middleware struct {
}
// Next calls the next workflow handler.
// It's an important function controlling the workflow of the server request execution.
func (m *Middleware) Next() {
var item *handlerParsedItem
var loop = true

View File

@ -32,7 +32,7 @@ var (
// to given struct, and then calls gvalid.CheckStruct validating the struct according
// to the validation tag of the struct.
//
// See GetStruct, gvalid.CheckStruct.
// See r.GetStruct, gvalid.CheckStruct.
func (r *Request) Parse(pointer interface{}) error {
if err := r.GetStruct(pointer); err != nil {
return err
@ -45,7 +45,7 @@ func (r *Request) Parse(pointer interface{}) error {
// Get is alias of GetRequest, which is one of the most commonly used functions for
// retrieving parameter.
// See GetRequest.
// See r.GetRequest.
func (r *Request) Get(key string, def ...interface{}) interface{} {
return r.GetRequest(key, def...)
}
@ -58,17 +58,20 @@ func (r *Request) GetVar(key string, def ...interface{}) *gvar.Var {
// GetRaw is alias of GetBody.
// See GetBody.
// Deprecated.
func (r *Request) GetRaw() []byte {
return r.GetBody()
}
// GetRawString is alias of GetBodyString.
// See GetBodyString.
// Deprecated.
func (r *Request) GetRawString() string {
return r.GetBodyString()
}
// GetRaw retrieves and returns request body content as bytes.
// GetBody retrieves and returns request body content as bytes.
// It can be called multiple times retrieving the same body content.
func (r *Request) GetBody() []byte {
if r.bodyContent == nil {
r.bodyContent, _ = ioutil.ReadAll(r.Body)
@ -77,92 +80,127 @@ func (r *Request) GetBody() []byte {
return r.bodyContent
}
// GetRawString retrieves and returns request body content as string.
// GetBodyString retrieves and returns request body content as string.
// It can be called multiple times retrieving the same body content.
func (r *Request) GetBodyString() string {
return gconv.UnsafeBytesToStr(r.GetRaw())
return gconv.UnsafeBytesToStr(r.GetBody())
}
// GetJson parses current request content as JSON format, and returns the JSON object.
// Note that the request content is read from request BODY, not from any field of FORM.
func (r *Request) GetJson() (*gjson.Json, error) {
return gjson.LoadJson(r.GetRaw())
return gjson.LoadJson(r.GetBody())
}
// GetString is an alias and convenient function for GetRequestString.
// See GetRequestString.
func (r *Request) GetString(key string, def ...interface{}) string {
return r.GetRequestString(key, def...)
}
// GetBool is an alias and convenient function for GetRequestBool.
// See GetRequestBool.
func (r *Request) GetBool(key string, def ...interface{}) bool {
return r.GetRequestBool(key, def...)
}
// GetInt is an alias and convenient function for GetRequestInt.
// See GetRequestInt.
func (r *Request) GetInt(key string, def ...interface{}) int {
return r.GetRequestInt(key, def...)
}
// GetInt32 is an alias and convenient function for GetRequestInt32.
// See GetRequestInt32.
func (r *Request) GetInt32(key string, def ...interface{}) int32 {
return r.GetRequestInt32(key, def...)
}
// GetInt64 is an alias and convenient function for GetRequestInt64.
// See GetRequestInt64.
func (r *Request) GetInt64(key string, def ...interface{}) int64 {
return r.GetRequestInt64(key, def...)
}
// GetInts is an alias and convenient function for GetRequestInts.
// See GetRequestInts.
func (r *Request) GetInts(key string, def ...interface{}) []int {
return r.GetRequestInts(key, def...)
}
// GetUint is an alias and convenient function for GetRequestUint.
// See GetRequestUint.
func (r *Request) GetUint(key string, def ...interface{}) uint {
return r.GetRequestUint(key, def...)
}
// GetUint32 is an alias and convenient function for GetRequestUint32.
// See GetRequestUint32.
func (r *Request) GetUint32(key string, def ...interface{}) uint32 {
return r.GetRequestUint32(key, def...)
}
// GetUint64 is an alias and convenient function for GetRequestUint64.
// See GetRequestUint64.
func (r *Request) GetUint64(key string, def ...interface{}) uint64 {
return r.GetRequestUint64(key, def...)
}
// GetFloat32 is an alias and convenient function for GetRequestFloat32.
// See GetRequestFloat32.
func (r *Request) GetFloat32(key string, def ...interface{}) float32 {
return r.GetRequestFloat32(key, def...)
}
// GetFloat64 is an alias and convenient function for GetRequestFloat64.
// See GetRequestFloat64.
func (r *Request) GetFloat64(key string, def ...interface{}) float64 {
return r.GetRequestFloat64(key, def...)
}
// GetFloats is an alias and convenient function for GetRequestFloats.
// See GetRequestFloats.
func (r *Request) GetFloats(key string, def ...interface{}) []float64 {
return r.GetRequestFloats(key, def...)
}
// GetArray is an alias and convenient function for GetRequestArray.
// See GetRequestArray.
func (r *Request) GetArray(key string, def ...interface{}) []string {
return r.GetRequestArray(key, def...)
}
// GetStrings is an alias and convenient function for GetRequestStrings.
// See GetRequestStrings.
func (r *Request) GetStrings(key string, def ...interface{}) []string {
return r.GetRequestStrings(key, def...)
}
// GetInterfaces is an alias and convenient function for GetRequestInterfaces.
// See GetRequestInterfaces.
func (r *Request) GetInterfaces(key string, def ...interface{}) []interface{} {
return r.GetRequestInterfaces(key, def...)
}
// GetMap is an alias and convenient function for GetRequestMap.
// See GetRequestMap.
func (r *Request) GetMap(def ...map[string]interface{}) map[string]interface{} {
return r.GetRequestMap(def...)
}
// GetMapStrStr is an alias and convenient function for GetRequestMapStrStr.
// See GetRequestMapStrStr.
func (r *Request) GetMapStrStr(def ...map[string]interface{}) map[string]string {
return r.GetRequestMapStrStr(def...)
}
// GetStruct is alias of GetRequestToStruct.
// See GetRequestToStruct.
// GetStruct is an alias and convenient function for GetRequestStruct.
// See GetRequestStruct.
func (r *Request) GetStruct(pointer interface{}, mapping ...map[string]string) error {
return r.GetRequestStruct(pointer, mapping...)
}
// GetToStruct is alias of GetRequestToStruct.
// GetToStruct is an alias and convenient function for GetRequestStruct.
// See GetRequestToStruct.
// Deprecated.
func (r *Request) GetToStruct(pointer interface{}, mapping ...map[string]string) error {
@ -184,7 +222,7 @@ func (r *Request) parseQuery() {
}
}
// ParseRaw parses the request raw data into r.rawMap.
// parseBody parses the request raw data into r.rawMap.
// Note that it also supports JSON data from client request.
func (r *Request) parseBody() {
if r.parsedBody {
@ -289,7 +327,7 @@ func (r *Request) GetMultipartForm() *multipart.Form {
return r.MultipartForm
}
// GetMultipartFiles returns the post files array.
// GetMultipartFiles parses and returns the post files array.
// Note that the request form should be type of multipart.
func (r *Request) GetMultipartFiles(name string) []*multipart.FileHeader {
form := r.GetMultipartForm()

View File

@ -11,8 +11,9 @@ import (
"github.com/gogf/gf/container/gvar"
)
// Context retrieves and returns the request's context.
// Context is alias for function GetCtx.
// This function overwrites the http.Request.Context function.
// See GetCtx.
func (r *Request) Context() context.Context {
if r.context == nil {
r.context = r.Request.Context()
@ -20,13 +21,14 @@ func (r *Request) Context() context.Context {
return r.context
}
// GetCtx is alias for function Context.
// See Context.
// GetCtx retrieves and returns the request's context.
func (r *Request) GetCtx() context.Context {
return r.Context()
}
// GetCtxVar retrieves and returns a Var with given key name.
// The optional parameter <def> specifies the default value of the Var if given <key>
// does not exist in the context.
func (r *Request) GetCtxVar(key interface{}, def ...interface{}) *gvar.Var {
value := r.Context().Value(key)
if value == nil && len(def) > 0 {

View File

@ -30,10 +30,7 @@ type UploadFiles []*UploadFile
//
// The parameter <dirPath> should be a directory path or it returns error.
//
// The parameter <randomlyRename> specifies whether randomly renames the file name, which
// make sense if the <path> is a directory.
//
// Note that it will overwrite the target file if there's already a same name file exist.
// Note that it will OVERWRITE the target file if there's already a same name file exist.
func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename string, err error) {
if f == nil {
return "", errors.New("file is empty, maybe you retrieve it from invalid field name or form enctype")
@ -93,6 +90,8 @@ func (fs UploadFiles) Save(dirPath string, randomlyRename ...bool) (filenames []
// This function is used for retrieving single uploading file object, which is
// uploaded using multipart form content type.
//
// It returns nil if retrieving failed or no form file with given name posted.
//
// Note that the <name> is the file field name of the multipart form from client.
func (r *Request) GetUploadFile(name string) *UploadFile {
uploadFiles := r.GetUploadFiles(name)
@ -106,6 +105,8 @@ func (r *Request) GetUploadFile(name string) *UploadFile {
// This function is used for retrieving multiple uploading file objects, which are
// uploaded using multipart form content type.
//
// It returns nil if retrieving failed or no form file with given name posted.
//
// Note that the <name> is the file field name of the multipart form from client.
func (r *Request) GetUploadFiles(name string) UploadFiles {
multipartFiles := r.GetMultipartFiles(name)

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