mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
Merge branch 'master' into master
This commit is contained in:
@ -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))
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
|
||||
|
||||
@ -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"/>
|
||||
|
||||
68
README.MD
68
README.MD
@ -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.
|
||||
|
||||
55
README_ZH.MD
55
README_ZH.MD
@ -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>
|
||||
|
||||
|
||||
# 文档
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -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})
|
||||
})
|
||||
}
|
||||
|
||||
@ -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"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -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"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -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})
|
||||
})
|
||||
}
|
||||
|
||||
@ -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"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -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{}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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})
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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...),
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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] != "" {
|
||||
|
||||
@ -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] != "" {
|
||||
|
||||
@ -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] != "" {
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
147
database/gdb/gdb_model_time.go
Normal file
147
database/gdb/gdb_model_time.go
Normal 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]
|
||||
}
|
||||
@ -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)...,
|
||||
)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
289
database/gdb/gdb_unit_z_mysql_internal_test.go
Normal file
289
database/gdb/gdb_unit_z_mysql_internal_test.go
Normal 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",
|
||||
)
|
||||
})
|
||||
}
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
370
database/gdb/gdb_unit_z_mysql_time_test.go
Normal file
370
database/gdb/gdb_unit_z_mysql_time_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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...)
|
||||
|
||||
@ -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
2
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/gogf/gf
|
||||
|
||||
go 1.13
|
||||
go 1.11
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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, ""),
|
||||
)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
Reference in New Issue
Block a user