From 5dc7ab48425d9255795ff611a17a389d095a7e00 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 10:41:12 +0800 Subject: [PATCH 01/22] add t list. --- container/glist/glist_t.go | 688 +++++++++++++++++++ container/glist/glist_z_bench_t_test.go | 61 ++ container/glist/glist_z_example_t_test.go | 689 ++++++++++++++++++++ container/glist/glist_z_unit_t_test.go | 761 ++++++++++++++++++++++ 4 files changed, 2199 insertions(+) create mode 100644 container/glist/glist_t.go create mode 100644 container/glist/glist_z_bench_t_test.go create mode 100644 container/glist/glist_z_example_t_test.go create mode 100644 container/glist/glist_z_unit_t_test.go diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go new file mode 100644 index 00000000000..e32fa504632 --- /dev/null +++ b/container/glist/glist_t.go @@ -0,0 +1,688 @@ +// Copyright GoFrame Author(https://goframe.org). 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 l file, +// You can obtain one at https://github.com/gogf/gf. +// + +// Package glist provides most commonly used doubly linked list container which also supports +// concurrent-safe/unsafe switch feature. +package glist + +import ( + "bytes" + "container/list" + + "github.com/gogf/gf/v2/internal/deepcopy" + "github.com/gogf/gf/v2/internal/json" + "github.com/gogf/gf/v2/internal/rwmutex" + "github.com/gogf/gf/v2/util/gconv" +) + +// TElement is an element of a linked list. +type TElement[T any] struct { + // Next and previous pointers in the doubly-linked list of elements. + // To simplify the implementation, internally a list l is implemented + // as a ring, such that &l.root is both the next element of the last + // list element (l.Back()) and the previous element of the first list + // element (l.Front()). + next, prev *TElement[T] + + // The list to which this element belongs. + list *TList[T] + + // The value stored with this element. + Value T +} + +// Next returns the next list element or nil. +func (e *TElement[T]) Next() *TElement[T] { + if p := e.next; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// Prev returns the previous list element or nil. +func (e *TElement[T]) Prev() *TElement[T] { + if p := e.prev; e.list != nil && p != &e.list.root { + return p + } + return nil +} + +// List is a doubly linked list containing a concurrent-safe/unsafe switch. +// The switch should be set when its initialization and cannot be changed then. + +type TList[T any] struct { + mu rwmutex.RWMutex + root TElement[T] // sentinel list element, only &root, root.prev, and root.next are used + len int // current list length excluding (this) sentinel element +} + +// New creates and returns a new empty doubly linked list. +func NewT[T any](safe ...bool) *TList[T] { + l := &TList[T]{ + mu: rwmutex.Create(safe...), + } + return l.init() +} + +// NewFrom creates and returns a list from a copy of given slice `array`. +// The parameter `safe` is used to specify whether using list in concurrent-safety, +// which is false in default. +func NewTFrom[T any](array []T, safe ...bool) *TList[T] { + l := NewT[T](safe...) + for _, v := range array { + l.insertValue(v, l.root.prev) + } + return l +} + +// PushFront inserts a new element `e` with value `v` at the front of list `l` and returns `e`. +func (l *TList[T]) PushFront(v T) (e *TElement[T]) { + l.mu.Lock() + l.lazyInit() + e = l.insertValue(v, &l.root) + l.mu.Unlock() + return +} + +// PushBack inserts a new element `e` with value `v` at the back of list `l` and returns `e`. +func (l *TList[T]) PushBack(v T) (e *TElement[T]) { + l.mu.Lock() + l.lazyInit() + e = l.insertValue(v, l.root.prev) + l.mu.Unlock() + return +} + +// PushFronts inserts multiple new elements with values `values` at the front of list `l`. +func (l *TList[T]) PushFronts(values []T) { + l.mu.Lock() + l.lazyInit() + for _, v := range values { + l.insertValue(v, &l.root) + } + l.mu.Unlock() +} + +// PushBacks inserts multiple new elements with values `values` at the back of list `l`. +func (l *TList[T]) PushBacks(values []T) { + l.mu.Lock() + l.lazyInit() + for _, v := range values { + l.insertValue(v, l.root.prev) + } + l.mu.Unlock() +} + +// PopBack removes the element from back of `l` and returns the value of the element. +func (l *TList[T]) PopBack() (value T) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + if l.len == 0 { + return + } + return l.remove(l.root.prev) +} + +// PopFront removes the element from front of `l` and returns the value of the element. +func (l *TList[T]) PopFront() (value any) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + if l.len == 0 { + return + } + return l.remove(l.root.next) +} + +// PopBacks removes `max` elements from back of `l` +// and returns values of the removed elements as slice. +func (l *TList[T]) PopBacks(max int) (values []T) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + + length := l.len + if length > 0 { + if max > 0 && max < length { + length = max + } + values = make([]T, length) + for i := 0; i < length; i++ { + values[i] = l.remove(l.root.prev) + } + } + return +} + +// PopFronts removes `max` elements from front of `l` +// and returns values of the removed elements as slice. +func (l *TList[T]) PopFronts(max int) (values []T) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + + length := l.len + if length > 0 { + if max > 0 && max < length { + length = max + } + values = make([]T, length) + for i := 0; i < length; i++ { + values[i] = l.remove(l.root.next) + } + } + return +} + +// PopBackAll removes all elements from back of `l` +// and returns values of the removed elements as slice. +func (l *TList[T]) PopBackAll() []T { + return l.PopBacks(-1) +} + +// PopFrontAll removes all elements from front of `l` +// and returns values of the removed elements as slice. +func (l *TList[T]) PopFrontAll() []T { + return l.PopFronts(-1) +} + +// FrontAll copies and returns values of all elements from front of `l` as slice. +func (l *TList[T]) FrontAll() (values []T) { + l.mu.RLock() + defer l.mu.RUnlock() + l.lazyInit() + length := l.len + if length > 0 { + values = make([]T, length) + for i, e := 0, l.front(); i < length; i, e = i+1, e.Next() { + values[i] = e.Value + } + } + return +} + +// BackAll copies and returns values of all elements from back of `l` as slice. +func (l *TList[T]) BackAll() (values []T) { + l.mu.RLock() + defer l.mu.RUnlock() + l.lazyInit() + length := l.len + if length > 0 { + values = make([]T, length) + for i, e := 0, l.back(); i < length; i, e = i+1, e.Prev() { + values[i] = e.Value + } + } + return +} + +// FrontValue returns value of the first element of `l` or nil if the list is empty. +func (l *TList[T]) FrontValue() (value T) { + l.mu.RLock() + defer l.mu.RUnlock() + l.lazyInit() + if e := l.front(); e != nil { + value = e.Value + } + return +} + +// BackValue returns value of the last element of `l` or nil if the list is empty. +func (l *TList[T]) BackValue() (value T) { + l.mu.RLock() + defer l.mu.RUnlock() + l.lazyInit() + if e := l.back(); e != nil { + value = e.Value + } + return +} + +// Front returns the first element of list `l` or nil if the list is empty. +func (l *TList[T]) Front() (e *TElement[T]) { + l.mu.RLock() + defer l.mu.RUnlock() + + l.lazyInit() + + e = l.front() + return +} + +// Back returns the last element of list `l` or nil if the list is empty. +func (l *TList[T]) Back() (e *TElement[T]) { + l.mu.RLock() + defer l.mu.RUnlock() + l.lazyInit() + + e = l.back() + return +} + +// Len returns the number of elements of list `l`. +// The complexity is O(1). +func (l *TList[T]) Len() (length int) { + l.mu.RLock() + defer l.mu.RUnlock() + + l.lazyInit() + + length = l.len + return +} + +// Size is alias of Len. +func (l *TList[T]) Size() int { + return l.Len() +} + +// MoveBefore moves element `e` to its new position before `p`. +// If `e` or `p` is not an element of `l`, or `e` == `p`, the list is not modified. +// The element and `p` must not be nil. +func (l *TList[T]) MoveBefore(e, p *TElement[T]) { + l.mu.Lock() + defer l.mu.Unlock() + + l.lazyInit() + + if e.list != l || e == p || p.list != l { + return + } + l.move(e, p.prev) +} + +// MoveAfter moves element `e` to its new position after `p`. +// If `e` or `p` is not an element of `l`, or `e` == `p`, the list is not modified. +// The element and `p` must not be nil. +func (l *TList[T]) MoveAfter(e, p *TElement[T]) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + if e.list != l || e == p || p.list != l { + return + } + l.move(e, p) +} + +// MoveToFront moves element `e` to the front of list `l`. +// If `e` is not an element of `l`, the list is not modified. +// The element must not be nil. +func (l *TList[T]) MoveToFront(e *TElement[T]) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + + if e.list != l || l.root.next == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, &l.root) +} + +// MoveToBack moves element `e` to the back of list `l`. +// If `e` is not an element of `l`, the list is not modified. +// The element must not be nil. +func (l *TList[T]) MoveToBack(e *TElement[T]) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + + if e.list != l || l.root.prev == e { + return + } + // see comment in List.Remove about initialization of l + l.move(e, l.root.prev) +} + +// PushBackList inserts a copy of an other list at the back of list `l`. +// The lists `l` and `other` may be the same, but they must not be nil. +func (l *TList[T]) PushBackList(other *TList[T]) { + if l != other { + other.mu.RLock() + defer other.mu.RUnlock() + } + l.mu.Lock() + defer l.mu.Unlock() + + l.lazyInit() + + for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { + l.insertValue(e.Value, l.root.prev) + } +} + +// PushFrontList inserts a copy of an other list at the front of list `l`. +// The lists `l` and `other` may be the same, but they must not be nil. +func (l *TList[T]) PushFrontList(other *TList[T]) { + if l != other { + other.mu.RLock() + defer other.mu.RUnlock() + } + l.mu.Lock() + defer l.mu.Unlock() + + l.lazyInit() + + for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { + l.insertValue(e.Value, &l.root) + } +} + +// InsertAfter inserts a new element `e` with value `v` immediately after `p` and returns `e`. +// If `p` is not an element of `l`, the list is not modified. +// The `p` must not be nil. +func (l *TList[T]) InsertAfter(p *TElement[T], v T) (e *TElement[T]) { + l.mu.Lock() + defer l.mu.Unlock() + + l.lazyInit() + if p.list != l { + return nil + } + e = l.insertValue(v, p) + return +} + +// InsertBefore inserts a new element `e` with value `v` immediately before `p` and returns `e`. +// If `p` is not an element of `l`, the list is not modified. +// The `p` must not be nil. +func (l *TList[T]) InsertBefore(p *TElement[T], v T) (e *TElement[T]) { + l.mu.Lock() + defer l.mu.Unlock() + + l.lazyInit() + if p.list != l { + return nil + } + e = l.insertValue(v, p.prev) + return +} + +// Remove removes `e` from `l` if `e` is an element of list `l`. +// It returns the element value e.Value. +// The element must not be nil. +func (l *TList[T]) Remove(e *TElement[T]) (value T) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + return l.remove(e) +} + +// Removes removes multiple elements `es` from `l` if `es` are elements of list `l`. +func (l *TList[T]) Removes(es []*TElement[T]) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + for _, e := range es { + l.remove(e) + } +} + +// RemoveAll removes all elements from list `l`. +func (l *TList[T]) RemoveAll() { + l.mu.Lock() + l.init() + l.mu.Unlock() +} + +// Clear is alias of RemoveAll. +func (l *TList[T]) Clear() { + l.RemoveAll() +} + +// ToList converts TList[T] to list.List +func (l *TList[T]) ToList() *list.List { + nl := list.New() + for e, p := l.Front(), l.Back(); e != p; e = e.Next() { + nl.PushBack(e.Value) + } + return nl +} + +// AppendList append list.List to the end +func (l *TList[T]) AppendList(nl *list.List) { + l.lazyInit() + if nl.Len() == 0 { + return + } + for e, p := nl.Front(), nl.Back(); e != p; e = e.Next() { + v, _ := e.Value.(T) + l.PushBack(v) + } +} + +// AssignList assigns list.List to now TList[T]. +// It will clear TList[T] first, and append the list.List. +func (l *TList[T]) AssignList(nl *list.List) { + l.init() + if nl.Len() == 0 { + return + } + for e, p := nl.Front(), nl.Back(); e != p; e = e.Next() { + v, _ := e.Value.(T) + l.PushBack(v) + } +} + +// RLockFunc locks reading with given callback function `f` within RWMutex.RLock. +func (l *TList[T]) RLockFunc(f func(list *list.List)) { + l.mu.RLock() + defer l.mu.RUnlock() + + f(l.ToList()) +} + +// LockFunc locks writing with given callback function `f` within RWMutex.Lock. +func (l *TList[T]) LockFunc(f func(list *list.List)) { + l.mu.Lock() + defer l.mu.Unlock() + nl := l.ToList() + f(nl) + l.AssignList(nl) +} + +// Iterator is alias of IteratorAsc. +func (l *TList[T]) Iterator(f func(e *TElement[T]) bool) { + l.IteratorAsc(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 *TList[T]) IteratorAsc(f func(e *TElement[T]) bool) { + l.mu.RLock() + defer l.mu.RUnlock() + l.lazyInit() + length := l.len + if length > 0 { + for i, e := 0, l.Front(); i < length; i, e = i+1, e.Next() { + if !f(e) { + break + } + } + } +} + +// 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 *TList[T]) IteratorDesc(f func(e *TElement[T]) bool) { + l.mu.RLock() + defer l.mu.RUnlock() + l.lazyInit() + length := l.len + if length > 0 { + for i, e := 0, l.Back(); i < length; i, e = i+1, e.Prev() { + if !f(e) { + break + } + } + } +} + +// Join joins list elements with a string `glue`. +func (l *TList[T]) Join(glue string) string { + l.mu.RLock() + defer l.mu.RUnlock() + l.lazyInit() + + buffer := bytes.NewBuffer(nil) + length := l.Len() + if length > 0 { + for i, e := 0, l.Front(); i < length; i, e = i+1, e.Next() { + buffer.WriteString(gconv.String(e.Value)) + if i != length-1 { + buffer.WriteString(glue) + } + } + } + return buffer.String() +} + +// String returns current list as a string. +func (l *TList[T]) String() string { + if l == nil { + return "" + } + return "[" + l.Join(",") + "]" +} + +// MarshalJSON implements the interface MarshalJSON for json.Marshal. +func (l TList[T]) MarshalJSON() ([]byte, error) { + return json.Marshal(l.FrontAll()) +} + +// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. +func (l *TList[T]) UnmarshalJSON(b []byte) error { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + + var array []T + if err := json.UnmarshalUseNumber(b, &array); err != nil { + return err + } + l.PushBacks(array) + return nil +} + +// UnmarshalValue is an interface implement which sets any type of value for list. +func (l *TList[T]) UnmarshalValue(value any) (err error) { + l.mu.Lock() + defer l.mu.Unlock() + l.lazyInit() + var array []T + switch value.(type) { + case string, []byte: + err = json.UnmarshalUseNumber(gconv.Bytes(value), &array) + default: + anyArray := gconv.SliceAny(value) + if err = gconv.Scan(anyArray, &array); err != nil { + return + } + } + l.PushBacks(array) + return err +} + +// DeepCopy implements interface for deep copy of current type. +func (l *TList[T]) DeepCopy() any { + if l == nil { + return nil + } + + l.mu.RLock() + defer l.mu.RUnlock() + + l.lazyInit() + + var ( + length = l.Len() + values = make([]any, length) + ) + if length > 0 { + for i, e := 0, l.Front(); i < length; i, e = i+1, e.Next() { + values[i] = deepcopy.Copy(e.Value) + } + } + return NewTFrom(values, l.mu.IsSafe()) +} + +// Init initializes or clears list l. +func (l *TList[T]) init() *TList[T] { + l.root.next = &l.root + l.root.prev = &l.root + l.len = 0 + return l +} + +// lazyInit lazily initializes a zero List value. +func (l *TList[T]) lazyInit() { + if l.root.next == nil { + l.init() + } +} + +// insert inserts e after at, increments l.len, and returns e. +func (l *TList[T]) insert(e, at *TElement[T]) *TElement[T] { + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e + e.list = l + l.len++ + return e +} + +// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). +func (l *TList[T]) insertValue(v T, at *TElement[T]) *TElement[T] { + return l.insert(&TElement[T]{Value: v}, at) +} + +// remove removes e from its list, decrements l.len +func (l *TList[T]) remove(e *TElement[T]) (val T) { + if e.list != l { + return + } + e.prev.next = e.next + e.next.prev = e.prev + e.next = nil // avoid memory leaks + e.prev = nil // avoid memory leaks + e.list = nil + l.len-- + + return e.Value +} + +// move moves e to next to at. +func (l *TList[T]) move(e, at *TElement[T]) { + if e == at { + return + } + e.prev.next = e.next + e.next.prev = e.prev + + e.prev = at + e.next = at.next + e.prev.next = e + e.next.prev = e +} + +// Front returns the first element of list l or nil if the list is empty. +func (l *TList[T]) front() *TElement[T] { + if l.len == 0 { + return nil + } + return l.root.next +} + +// Back returns the last element of list l or nil if the list is empty. +func (l *TList[T]) back() *TElement[T] { + if l.len == 0 { + return nil + } + return l.root.prev +} diff --git a/container/glist/glist_z_bench_t_test.go b/container/glist/glist_z_bench_t_test.go new file mode 100644 index 00000000000..736bd4a0c7d --- /dev/null +++ b/container/glist/glist_z_bench_t_test.go @@ -0,0 +1,61 @@ +// Copyright GoFrame Author(https://goframe.org). 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. + +// go test *.go -bench=".*" -benchmem + +package glist + +import ( + "testing" +) + +var ( + lt = NewT[any](true) +) + +func Benchmark_T_PushBack(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + lt.PushBack(i) + i++ + } + }) +} + +func Benchmark_T_PushFront(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + i := 0 + for pb.Next() { + lt.PushFront(i) + i++ + } + }) +} + +func Benchmark_T_Len(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + lt.Len() + } + }) +} + +func Benchmark_T_PopFront(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + lt.PopFront() + } + }) +} + +func Benchmark_T_PopBack(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + lt.PopBack() + } + }) +} diff --git a/container/glist/glist_z_example_t_test.go b/container/glist/glist_z_example_t_test.go new file mode 100644 index 00000000000..8654756b8b4 --- /dev/null +++ b/container/glist/glist_z_example_t_test.go @@ -0,0 +1,689 @@ +// Copyright GoFrame Author(https://goframe.org). 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 glist_test + +import ( + "container/list" + "fmt" + + "github.com/gogf/gf/v2/container/garray" + "github.com/gogf/gf/v2/container/glist" + "github.com/gogf/gf/v2/frame/g" +) + +func ExampleNewT() { + n := 10 + l := glist.NewT[any]() + for i := 0; i < n; i++ { + l.PushBack(i) + } + + fmt.Println(l.Len()) + fmt.Println(l) + fmt.Println(l.FrontAll()) + fmt.Println(l.BackAll()) + + for i := 0; i < n; i++ { + fmt.Print(l.PopFront()) + } + + fmt.Println() + fmt.Println(l.Len()) + + // Output: + // 10 + // [0,1,2,3,4,5,6,7,8,9] + // [0 1 2 3 4 5 6 7 8 9] + // [9 8 7 6 5 4 3 2 1 0] + // 0123456789 + // 0 +} + +func ExampleNewTFrom() { + n := 10 + l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + fmt.Println(l.FrontAll()) + fmt.Println(l.BackAll()) + + for i := 0; i < n; i++ { + fmt.Print(l.PopFront()) + } + + fmt.Println() + fmt.Println(l.Len()) + + // Output: + // 10 + // [1,2,3,4,5,6,7,8,9,10] + // [1 2 3 4 5 6 7 8 9 10] + // [10 9 8 7 6 5 4 3 2 1] + // 12345678910 + // 0 +} + +func ExampleTList_PushFront() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + + l.PushFront(0) + + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 6 + // [0,1,2,3,4,5] +} + +func ExampleTList_PushBack() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + + l.PushBack(6) + + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 6 + // [1,2,3,4,5,6] +} + +func ExampleTList_PushFronts() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + + l.PushFronts(g.Slice{0, -1, -2, -3, -4}) + + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 10 + // [-4,-3,-2,-1,0,1,2,3,4,5] +} + +func ExampleTList_PushBacks() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + + l.PushBacks(g.Slice{6, 7, 8, 9, 10}) + + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 10 + // [1,2,3,4,5,6,7,8,9,10] +} + +func ExampleTList_PopBack() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + fmt.Println(l.PopBack()) + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 5 + // 4 + // [1,2,3,4] +} + +func ExampleTList_PopFront() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + fmt.Println(l.PopFront()) + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 1 + // 4 + // [2,3,4,5] +} + +func ExampleTList_PopBacks() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + fmt.Println(l.PopBacks(2)) + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // [5 4] + // 3 + // [1,2,3] +} + +func ExampleTList_PopFronts() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + fmt.Println(l.PopFronts(2)) + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // [1 2] + // 3 + // [3,4,5] +} + +func ExampleTList_PopBackAll() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + fmt.Println(l.PopBackAll()) + fmt.Println(l.Len()) + + // Output: + // 5 + // [1,2,3,4,5] + // [5 4 3 2 1] + // 0 +} + +func ExampleTList_PopFrontAll() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + fmt.Println(l.PopFrontAll()) + fmt.Println(l.Len()) + + // Output: + // 5 + // [1,2,3,4,5] + // [1 2 3 4 5] + // 0 +} + +func ExampleTList_FrontAll() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l) + fmt.Println(l.FrontAll()) + + // Output: + // [1,2,3,4,5] + // [1 2 3 4 5] +} + +func ExampleTList_BackAll() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l) + fmt.Println(l.BackAll()) + + // Output: + // [1,2,3,4,5] + // [5 4 3 2 1] +} + +func ExampleTList_FrontValue() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l) + fmt.Println(l.FrontValue()) + + // Output: + // [1,2,3,4,5] + // 1 +} + +func ExampleTList_BackValue() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l) + fmt.Println(l.BackValue()) + + // Output: + // [1,2,3,4,5] + // 5 +} + +func ExampleTList_Front() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Front().Value) + fmt.Println(l) + + e := l.Front() + l.InsertBefore(e, 0) + l.InsertAfter(e, "a") + + fmt.Println(l) + + // Output: + // 1 + // [1,2,3,4,5] + // [0,1,a,2,3,4,5] +} + +func ExampleTList_Back() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Back().Value) + fmt.Println(l) + + e := l.Back() + l.InsertBefore(e, "a") + l.InsertAfter(e, 6) + + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // [1,2,3,4,a,5,6] +} + +func ExampleTList_Len() { + l := glist.NewTFrom[any](g.Slice{1, 2, 3, 4, 5}) + + fmt.Println(l.Len()) + + // Output: + // 5 +} + +func ExampleTList_Size() { + l := glist.NewTFrom[any](g.Slice{1, 2, 3, 4, 5}) + + fmt.Println(l.Size()) + + // Output: + // 5 +} + +func ExampleTList_MoveBefore() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // element of `l` + e := l.PushBack(6) + fmt.Println(l.Size()) + fmt.Println(l) + + l.MoveBefore(e, l.Front()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // not element of `l` + e = &glist.TElement[any]{Value: 7} + l.MoveBefore(e, l.Front()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 6 + // [1,2,3,4,5,6] + // 6 + // [6,1,2,3,4,5] + // 6 + // [6,1,2,3,4,5] +} + +func ExampleTList_MoveAfter() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // element of `l` + e := l.PushFront(0) + fmt.Println(l.Size()) + fmt.Println(l) + + l.MoveAfter(e, l.Back()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // not element of `l` + e = &glist.TElement[any]{Value: -1} + l.MoveAfter(e, l.Back()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 6 + // [0,1,2,3,4,5] + // 6 + // [1,2,3,4,5,0] + // 6 + // [1,2,3,4,5,0] +} + +func ExampleTList_MoveToFront() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // element of `l` + l.MoveToFront(l.Back()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // not element of `l` + e := &glist.TElement[any]{Value: 6} + l.MoveToFront(e) + + fmt.Println(l.Size()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 5 + // [5,1,2,3,4] + // 5 + // [5,1,2,3,4] +} + +func ExampleTList_MoveToBack() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // element of `l` + l.MoveToBack(l.Front()) + + fmt.Println(l.Size()) + fmt.Println(l) + + // not element of `l` + e := &glist.TElement[any]{Value: 0} + l.MoveToBack(e) + + fmt.Println(l.Size()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 5 + // [2,3,4,5,1] + // 5 + // [2,3,4,5,1] +} + +func ExampleTList_PushBackList() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Size()) + fmt.Println(l) + + other := glist.NewTFrom[any](g.Slice{6, 7, 8, 9, 10}) + + fmt.Println(other.Size()) + fmt.Println(other) + + l.PushBackList(other) + + fmt.Println(l.Size()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 5 + // [6,7,8,9,10] + // 10 + // [1,2,3,4,5,6,7,8,9,10] +} + +func ExampleTList_PushFrontList() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Size()) + fmt.Println(l) + + other := glist.NewTFrom[any](g.Slice{-4, -3, -2, -1, 0}) + + fmt.Println(other.Size()) + fmt.Println(other) + + l.PushFrontList(other) + + fmt.Println(l.Size()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 5 + // [-4,-3,-2,-1,0] + // 10 + // [-4,-3,-2,-1,0,1,2,3,4,5] +} + +func ExampleTList_InsertAfter() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + + l.InsertAfter(l.Front(), "a") + l.InsertAfter(l.Back(), "b") + + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 7 + // [1,a,2,3,4,5,b] +} + +func ExampleTList_InsertBefore() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + + l.InsertBefore(l.Front(), "a") + l.InsertBefore(l.Back(), "b") + + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 7 + // [a,1,2,3,4,b,5] +} + +func ExampleTList_Remove() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + + fmt.Println(l.Remove(l.Front())) + fmt.Println(l.Remove(l.Back())) + + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 1 + // 5 + // 3 + // [2,3,4] +} + +func ExampleTList_Removes() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + + l.Removes([]*glist.TElement[any]{l.Front(), l.Back()}) + + fmt.Println(l.Len()) + fmt.Println(l) + + // Output: + // 5 + // [1,2,3,4,5] + // 3 + // [2,3,4] +} + +func ExampleTList_RemoveAll() { + l := glist.NewTFrom[any](garray.NewArrayRange(1, 5, 1).Slice()) + + fmt.Println(l.Len()) + fmt.Println(l) + + l.RemoveAll() + + fmt.Println(l.Len()) + + // Output: + // 5 + // [1,2,3,4,5] + // 0 +} + +func ExampleTList_RLockFunc() { + // concurrent-safe list. + l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice(), true) + // iterate reading from head. + l.RLockFunc(func(list *list.List) { + length := list.Len() + if length > 0 { + for i, e := 0, list.Front(); i < length; i, e = i+1, e.Next() { + fmt.Print(e.Value) + } + } + }) + fmt.Println() + // iterate reading from tail. + l.RLockFunc(func(list *list.List) { + length := list.Len() + if length > 0 { + for i, e := 0, list.Back(); i < length; i, e = i+1, e.Prev() { + fmt.Print(e.Value) + } + } + }) + + fmt.Println() + // Output: + // 12345678910 + // 10987654321 +} + +func ExampleTList_IteratorAsc() { + // concurrent-safe list. + l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice(), true) + // iterate reading from head using IteratorAsc. + l.IteratorAsc(func(e *glist.TElement[any]) bool { + fmt.Print(e.Value) + return true + }) + + // Output: + // 12345678910 +} + +func ExampleTList_IteratorDesc() { + // concurrent-safe list. + l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice(), true) + // iterate reading from tail using IteratorDesc. + l.IteratorDesc(func(e *glist.TElement[any]) bool { + fmt.Print(e.Value) + return true + }) + // Output: + // 10987654321 +} + +func ExampleTList_LockFunc() { + // concurrent-safe list. + l := glist.NewTFrom[any](garray.NewArrayRange(1, 10, 1).Slice(), true) + // iterate writing from head. + l.LockFunc(func(list *list.List) { + length := list.Len() + if length > 0 { + for i, e := 0, list.Front(); i < length; i, e = i+1, e.Next() { + if e.Value == 6 { + e.Value = "M" + break + } + } + } + }) + fmt.Println(l) + + // Output: + // [1,2,3,4,5,M,7,8,9,10] +} + +func ExampleTList_Join() { + var l glist.List + l.PushBacks(g.Slice{"a", "b", "c", "d"}) + + fmt.Println(l.Join(",")) + + // Output: + // a,b,c,d +} diff --git a/container/glist/glist_z_unit_t_test.go b/container/glist/glist_z_unit_t_test.go new file mode 100644 index 00000000000..fcfb1fc3556 --- /dev/null +++ b/container/glist/glist_z_unit_t_test.go @@ -0,0 +1,761 @@ +// Copyright GoFrame Author(https://goframe.org). 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 glist + +import ( + "testing" + + "github.com/gogf/gf/v2/internal/json" + "github.com/gogf/gf/v2/test/gtest" + "github.com/gogf/gf/v2/util/gconv" +) + +func checkTListLen(t *gtest.T, l *TList[any], len int) bool { + if n := l.Len(); n != len { + t.Errorf("l.Len() = %d, want %d", n, len) + return false + } + return true +} + +func checkTListPointers(t *gtest.T, l *TList[any], es []*TElement[any]) { + if !checkTListLen(t, l, len(es)) { + return + } + + i := 0 + l.Iterator(func(e *TElement[any]) bool { + if e.Prev() != es[i].Prev() { + t.Errorf("list[%d].Prev = %p, want %p", i, e.Prev(), es[i].Prev()) + return false + } + if e.Next() != es[i].Next() { + t.Errorf("list[%d].Next = %p, want %p", i, e.Next(), es[i].Next()) + return false + } + i++ + return true + }) +} + +func TestTVar(t *testing.T) { + var l TList[any] + 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 TestTBasic(t *testing.T) { + l := NewT[any]() + 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 TestTList(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + checkTListPointers(t, l, []*TElement[any]{}) + + // Single element list + e := l.PushFront("a") + checkTListPointers(t, l, []*TElement[any]{e}) + l.MoveToFront(e) + checkTListPointers(t, l, []*TElement[any]{e}) + l.MoveToBack(e) + checkTListPointers(t, l, []*TElement[any]{e}) + l.Remove(e) + checkTListPointers(t, l, []*TElement[any]{}) + + // Bigger list + e2 := l.PushFront(2) + e1 := l.PushFront(1) + e3 := l.PushBack(3) + e4 := l.PushBack("banana") + checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4}) + + l.Remove(e2) + checkTListPointers(t, l, []*TElement[any]{e1, e3, e4}) + + l.MoveToFront(e3) // move from middle + checkTListPointers(t, l, []*TElement[any]{e3, e1, e4}) + + l.MoveToFront(e1) + l.MoveToBack(e3) // move from middle + checkTListPointers(t, l, []*TElement[any]{e1, e4, e3}) + + l.MoveToFront(e3) // move from back + checkTListPointers(t, l, []*TElement[any]{e3, e1, e4}) + l.MoveToFront(e3) // should be no-op + checkTListPointers(t, l, []*TElement[any]{e3, e1, e4}) + + l.MoveToBack(e3) // move from front + checkTListPointers(t, l, []*TElement[any]{e1, e4, e3}) + l.MoveToBack(e3) // should be no-op + checkTListPointers(t, l, []*TElement[any]{e1, e4, e3}) + + e2 = l.InsertBefore(e1, 2) // insert before front + checkTListPointers(t, l, []*TElement[any]{e2, e1, e4, e3}) + l.Remove(e2) + e2 = l.InsertBefore(e4, 2) // insert before middle + checkTListPointers(t, l, []*TElement[any]{e1, e2, e4, e3}) + l.Remove(e2) + e2 = l.InsertBefore(e3, 2) // insert before back + checkTListPointers(t, l, []*TElement[any]{e1, e4, e2, e3}) + l.Remove(e2) + + e2 = l.InsertAfter(e1, 2) // insert after front + checkTListPointers(t, l, []*TElement[any]{e1, e2, e4, e3}) + l.Remove(e2) + e2 = l.InsertAfter(e4, 2) // insert after middle + checkTListPointers(t, l, []*TElement[any]{e1, e4, e2, e3}) + l.Remove(e2) + e2 = l.InsertAfter(e3, 2) // insert after back + checkTListPointers(t, l, []*TElement[any]{e1, e4, e3, e2}) + l.Remove(e2) + + // Check standard iteration. + sum := 0 + for e := l.Front(); e != nil; e = e.Next() { + if i, ok := e.Value.(int); ok { + sum += i + } + } + if sum != 4 { + t.Errorf("sum over l = %d, want 4", sum) + } + + // Clear all elements by iterating + var next *TElement[any] + for e := l.Front(); e != nil; e = next { + next = e.Next() + l.Remove(e) + } + checkTListPointers(t, l, []*TElement[any]{}) + }) +} + +func checkTList(t *gtest.T, l *TList[any], es []any) { + if !checkTListLen(t, l, len(es)) { + return + } + + i := 0 + for e := l.Front(); e != nil; e = e.Next() { + + switch e.Value.(type) { + case int: + if le := e.Value.(int); le != es[i] { + t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i]) + } + // default string + default: + if le := e.Value.(string); le != es[i] { + t.Errorf("elt[%v].Value() = %v, want %v", i, le, es[i]) + } + } + + i++ + } + + // for e := l.Front(); e != nil; e = e.Next() { + // le := e.Value.(int) + // if le != es[i] { + // t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i]) + // } + // i++ + // } +} + +func TestTExtending(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l1 := NewT[any]() + l2 := NewT[any]() + + l1.PushBack(1) + l1.PushBack(2) + l1.PushBack(3) + + l2.PushBack(4) + l2.PushBack(5) + + l3 := NewT[any]() + l3.PushBackList(l1) + checkTList(t, l3, []any{1, 2, 3}) + l3.PushBackList(l2) + checkTList(t, l3, []any{1, 2, 3, 4, 5}) + + l3 = NewT[any]() + l3.PushFrontList(l2) + checkTList(t, l3, []any{4, 5}) + l3.PushFrontList(l1) + checkTList(t, l3, []any{1, 2, 3, 4, 5}) + + checkTList(t, l1, []any{1, 2, 3}) + checkTList(t, l2, []any{4, 5}) + + l3 = NewT[any]() + l3.PushBackList(l1) + checkTList(t, l3, []any{1, 2, 3}) + l3.PushBackList(l3) + checkTList(t, l3, []any{1, 2, 3, 1, 2, 3}) + + l3 = NewT[any]() + l3.PushFrontList(l1) + checkTList(t, l3, []any{1, 2, 3}) + l3.PushFrontList(l3) + checkTList(t, l3, []any{1, 2, 3, 1, 2, 3}) + + l3 = NewT[any]() + l1.PushBackList(l3) + checkTList(t, l1, []any{1, 2, 3}) + l1.PushFrontList(l3) + checkTList(t, l1, []any{1, 2, 3}) + }) +} + +func TestTRemove(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + e1 := l.PushBack(1) + e2 := l.PushBack(2) + checkTListPointers(t, l, []*TElement[any]{e1, e2}) + // e := l.Front() + // l.Remove(e) + // checkTListPointers(t, l, []*TElement[any]{e2}) + // l.Remove(e) + // checkTListPointers(t, l, []*TElement[any]{e2}) + }) +} + +func Test_T_Issue4103(t *testing.T) { + l1 := NewT[any]() + l1.PushBack(1) + l1.PushBack(2) + + l2 := NewT[any]() + l2.PushBack(3) + l2.PushBack(4) + + e := l1.Front() + l2.Remove(e) // l2 should not change because e is not an element of l2 + if n := l2.Len(); n != 2 { + t.Errorf("l2.Len() = %d, want 2", n) + } + + l1.InsertBefore(e, 8) + if n := l1.Len(); n != 3 { + t.Errorf("l1.Len() = %d, want 3", n) + } +} + +func Test_T_Issue6349(t *testing.T) { + l := NewT[any]() + l.PushBack(1) + l.PushBack(2) + + e := l.Front() + l.Remove(e) + if e.Value != 1 { + t.Errorf("e.value = %d, want 1", e.Value) + } + // if e.Next() != nil { + // t.Errorf("e.Next() != nil") + // } + // if e.Prev() != nil { + // t.Errorf("e.Prev() != nil") + // } +} + +func TestTMove(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + e1 := l.PushBack(1) + e2 := l.PushBack(2) + e3 := l.PushBack(3) + e4 := l.PushBack(4) + + l.MoveAfter(e3, e3) + checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4}) + l.MoveBefore(e2, e2) + checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4}) + + l.MoveAfter(e3, e2) + checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4}) + l.MoveBefore(e2, e3) + checkTListPointers(t, l, []*TElement[any]{e1, e2, e3, e4}) + + l.MoveBefore(e2, e4) + checkTListPointers(t, l, []*TElement[any]{e1, e3, e2, e4}) + e2, e3 = e3, e2 + + l.MoveBefore(e4, e1) + checkTListPointers(t, l, []*TElement[any]{e4, e1, e2, e3}) + e1, e2, e3, e4 = e4, e1, e2, e3 + + l.MoveAfter(e4, e1) + checkTListPointers(t, l, []*TElement[any]{e1, e4, e2, e3}) + e2, e3, e4 = e4, e2, e3 + + l.MoveAfter(e2, e3) + checkTListPointers(t, l, []*TElement[any]{e1, e3, e2, e4}) + e2, e3 = e3, e2 + }) +} + +// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List +func TestTZeroList(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var l1 = NewT[any]() + l1.PushFront(1) + checkTList(t, l1, []any{1}) + + var l2 = NewT[any]() + l2.PushBack(1) + checkTList(t, l2, []any{1}) + + var l3 = NewT[any]() + l3.PushFrontList(l1) + checkTList(t, l3, []any{1}) + + var l4 = NewT[any]() + l4.PushBackList(l2) + checkTList(t, l4, []any{1}) + }) +} + +// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l. +func TestTInsertBeforeUnknownMark(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + l.PushBack(1) + l.PushBack(2) + l.PushBack(3) + l.InsertBefore(new(TElement[any]), 1) + checkTList(t, l, []any{1, 2, 3}) + }) +} + +// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l. +func TestTInsertAfterUnknownMark(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + l.PushBack(1) + l.PushBack(2) + l.PushBack(3) + l.InsertAfter(new(TElement[any]), 1) + checkTList(t, l, []any{1, 2, 3}) + }) +} + +// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l. +func TestTMoveUnknownMark(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l1 := NewT[any]() + e1 := l1.PushBack(1) + + l2 := NewT[any]() + e2 := l2.PushBack(2) + + l1.MoveAfter(e1, e2) + checkTList(t, l1, []any{1}) + checkTList(t, l2, []any{2}) + + l1.MoveBefore(e1, e2) + checkTList(t, l1, []any{1}) + checkTList(t, l2, []any{2}) + }) +} + +func TestTList_RemoveAll(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + l.PushBack(1) + l.RemoveAll() + checkTList(t, l, []any{}) + l.PushBack(2) + checkTList(t, l, []any{2}) + }) +} + +func TestTList_PushFronts(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2} + l.PushFronts(a1) + checkTList(t, l, []any{2, 1}) + a1 = []any{3, 4, 5} + l.PushFronts(a1) + checkTList(t, l, []any{5, 4, 3, 2, 1}) + }) +} + +func TestTList_PushBacks(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2} + l.PushBacks(a1) + checkTList(t, l, []any{1, 2}) + a1 = []any{3, 4, 5} + l.PushBacks(a1) + checkTList(t, l, []any{1, 2, 3, 4, 5}) + }) +} + +func TestTList_PopBacks(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + a2 := []any{"a", "c", "b", "e"} + l.PushFronts(a1) + i1 := l.PopBacks(2) + t.Assert(i1, []any{1, 2}) + + l.PushBacks(a2) // 4.3,a,c,b,e + i1 = l.PopBacks(3) + t.Assert(i1, []any{"e", "b", "c"}) + }) +} + +func TestTList_PopFronts(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + i1 := l.PopFronts(2) + t.Assert(i1, []any{4, 3}) + t.Assert(l.Len(), 2) + }) +} + +func TestTList_PopBackAll(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + i1 := l.PopBackAll() + t.Assert(i1, []any{1, 2, 3, 4}) + t.Assert(l.Len(), 0) + }) +} + +func TestTList_PopFrontAll(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + i1 := l.PopFrontAll() + t.Assert(i1, []any{4, 3, 2, 1}) + t.Assert(l.Len(), 0) + }) +} + +func TestTList_FrontAll(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + i1 := l.FrontAll() + t.Assert(i1, []any{4, 3, 2, 1}) + t.Assert(l.Len(), 4) + }) +} + +func TestTList_BackAll(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + i1 := l.BackAll() + t.Assert(i1, []any{1, 2, 3, 4}) + t.Assert(l.Len(), 4) + }) +} + +func TestTList_FrontValue(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + l2 := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + i1 := l.FrontValue() + t.Assert(gconv.Int(i1), 4) + t.Assert(l.Len(), 4) + + i1 = l2.FrontValue() + t.Assert(i1, nil) + }) +} + +func TestTList_BackValue(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + l2 := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + i1 := l.BackValue() + t.Assert(gconv.Int(i1), 1) + t.Assert(l.Len(), 4) + + i1 = l2.FrontValue() + t.Assert(i1, nil) + }) +} + +func TestTList_Back(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + e1 := l.Back() + t.Assert(e1.Value, 1) + t.Assert(l.Len(), 4) + }) +} + +func TestTList_Size(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + t.Assert(l.Size(), 4) + l.PopFront() + t.Assert(l.Size(), 3) + }) +} + +func TestTList_Removes(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + e1 := l.Back() + l.Removes([]*TElement[any]{e1}) + t.Assert(l.Len(), 3) + + e2 := l.Back() + l.Removes([]*TElement[any]{e2}) + t.Assert(l.Len(), 2) + checkTList(t, l, []any{4, 3}) + }) +} + +func TestTList_Pop(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewTFrom([]any{1, 2, 3, 4, 5, 6, 7, 8, 9}) + + t.Assert(l.PopBack(), 9) + t.Assert(l.PopBacks(2), []any{8, 7}) + t.Assert(l.PopFront(), 1) + t.Assert(l.PopFronts(2), []any{2, 3}) + }) +} + +func TestTList_Clear(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + l.Clear() + t.Assert(l.Len(), 0) + }) +} + +func TestTList_IteratorAsc(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 5, 6, 3, 4} + l.PushFronts(a1) + e1 := l.Back() + fun1 := func(e *TElement[any]) bool { + return gconv.Int(e1.Value) > 2 + } + checkTList(t, l, []any{4, 3, 6, 5, 2, 1}) + l.IteratorAsc(fun1) + checkTList(t, l, []any{4, 3, 6, 5, 2, 1}) + }) +} + +func TestTList_IteratorDesc(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{1, 2, 3, 4} + l.PushFronts(a1) + e1 := l.Back() + fun1 := func(e *TElement[any]) bool { + return gconv.Int(e1.Value) > 6 + } + l.IteratorDesc(fun1) + t.Assert(l.Len(), 4) + checkTList(t, l, []any{4, 3, 2, 1}) + }) +} + +func TestTList_Iterator(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + a1 := []any{"a", "b", "c", "d", "e"} + l.PushFronts(a1) + e1 := l.Back() + fun1 := func(e *TElement[any]) bool { + return gconv.String(e1.Value) > "c" + } + checkTList(t, l, []any{"e", "d", "c", "b", "a"}) + l.Iterator(fun1) + checkTList(t, l, []any{"e", "d", "c", "b", "a"}) + }) +} + +func TestTList_Join(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewTFrom([]any{1, 2, "a", `"b"`, `\c`}) + t.Assert(l.Join(","), `1,2,a,"b",\c`) + t.Assert(l.Join("."), `1.2.a."b".\c`) + }) +} + +func TestTList_String(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewTFrom([]any{1, 2, "a", `"b"`, `\c`}) + t.Assert(l.String(), `[1,2,a,"b",\c]`) + }) +} + +func TestTList_Json(t *testing.T) { + // Marshal + gtest.C(t, func(t *gtest.T) { + a := []any{"a", "b", "c"} + l := NewT[any]() + l.PushBacks(a) + b1, err1 := json.Marshal(l) + b2, err2 := json.Marshal(a) + t.Assert(err1, err2) + t.Assert(b1, b2) + }) + // Unmarshal + gtest.C(t, func(t *gtest.T) { + a := []any{"a", "b", "c"} + l := NewT[any]() + b, err := json.Marshal(a) + t.AssertNil(err) + + err = json.UnmarshalUseNumber(b, l) + t.AssertNil(err) + t.Assert(l.FrontAll(), a) + }) + gtest.C(t, func(t *gtest.T) { + var l TList[any] + a := []any{"a", "b", "c"} + b, err := json.Marshal(a) + t.AssertNil(err) + + err = json.UnmarshalUseNumber(b, &l) + t.AssertNil(err) + t.Assert(l.FrontAll(), a) + }) +} + +func TestTList_UnmarshalValue(t *testing.T) { + type list struct { + Name string + List *TList[any] + } + // JSON + gtest.C(t, func(t *gtest.T) { + var tlist *list + err := gconv.Struct(map[string]any{ + "name": "john", + "list": []byte(`[1,2,3]`), + }, &tlist) + t.AssertNil(err) + t.Assert(tlist.Name, "john") + t.Assert(tlist.List.FrontAll(), []any{1, 2, 3}) + }) + // Map + gtest.C(t, func(t *gtest.T) { + var tlist *list + err := gconv.Struct(map[string]any{ + "name": "john", + "list": []any{1, 2, 3}, + }, &tlist) + t.AssertNil(err) + t.Assert(tlist.Name, "john") + t.Assert(tlist.List.FrontAll(), []any{1, 2, 3}) + }) +} + +func TestTList_DeepCopy(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewTFrom([]any{1, 2, "a", `"b"`, `\c`}) + copyList := l.DeepCopy() + cl := copyList.(*TList[any]) + cl.PopBack() + t.AssertNE(l.Size(), cl.Size()) + }) +} From 8c0c50be267e571ab37abd6afcf064fc95b616b5 Mon Sep 17 00:00:00 2001 From: Hunk Zhu <54zhua@gmail.com> Date: Sat, 18 Oct 2025 10:56:25 +0800 Subject: [PATCH 02/22] Update container/glist/glist_t.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- container/glist/glist_t.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index e32fa504632..5f5d1168cff 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -601,14 +601,14 @@ func (l *TList[T]) DeepCopy() any { var ( length = l.Len() - values = make([]any, length) + valuesT = make([]T, length) ) if length > 0 { for i, e := 0, l.Front(); i < length; i, e = i+1, e.Next() { - values[i] = deepcopy.Copy(e.Value) + valuesT[i] = deepcopy.Copy(e.Value).(T) } } - return NewTFrom(values, l.mu.IsSafe()) + return NewTFrom(valuesT, l.mu.IsSafe()) } // Init initializes or clears list l. From 0fa1c7bdc01b48077beb304530e8ab70962a0ece Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 10:56:35 +0800 Subject: [PATCH 03/22] improved --- container/glist/glist_t.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index e32fa504632..f421fd0d893 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -129,7 +129,7 @@ func (l *TList[T]) PopBack() (value T) { } // PopFront removes the element from front of `l` and returns the value of the element. -func (l *TList[T]) PopFront() (value any) { +func (l *TList[T]) PopFront() (value T) { l.mu.Lock() defer l.mu.Unlock() l.lazyInit() From e5f853ac9be69fa1dee32db183aecc64b859b697 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 18 Oct 2025 02:56:46 +0000 Subject: [PATCH 04/22] Apply gci import order changes --- container/glist/glist_t.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 5f5d1168cff..787271e0c4e 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -600,7 +600,7 @@ func (l *TList[T]) DeepCopy() any { l.lazyInit() var ( - length = l.Len() + length = l.Len() valuesT = make([]T, length) ) if length > 0 { From 98b305989cbea5c4d4b3e6a4266160a94639daaf Mon Sep 17 00:00:00 2001 From: Hunk Zhu <54zhua@gmail.com> Date: Sat, 18 Oct 2025 10:57:04 +0800 Subject: [PATCH 05/22] Update container/glist/glist_t.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- container/glist/glist_t.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 748976c7f9a..54930187472 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -551,7 +551,7 @@ func (l *TList[T]) String() string { } // MarshalJSON implements the interface MarshalJSON for json.Marshal. -func (l TList[T]) MarshalJSON() ([]byte, error) { +func (l *TList[T]) MarshalJSON() ([]byte, error) { return json.Marshal(l.FrontAll()) } From a7f6e0eb615b9e580d4824dd075253f925d8f672 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 10:58:16 +0800 Subject: [PATCH 06/22] Revert "Update container/glist/glist_t.go" This reverts commit 98b305989cbea5c4d4b3e6a4266160a94639daaf. --- container/glist/glist_t.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 54930187472..748976c7f9a 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -551,7 +551,7 @@ func (l *TList[T]) String() string { } // MarshalJSON implements the interface MarshalJSON for json.Marshal. -func (l *TList[T]) MarshalJSON() ([]byte, error) { +func (l TList[T]) MarshalJSON() ([]byte, error) { return json.Marshal(l.FrontAll()) } From 0cdf916839330243573de4241fe7235ebbe18f65 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 11:00:22 +0800 Subject: [PATCH 07/22] improved UnmarshalJSON/UnmarshalValue --- container/glist/glist_t.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 748976c7f9a..e025d68c729 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -557,10 +557,6 @@ func (l TList[T]) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal. func (l *TList[T]) UnmarshalJSON(b []byte) error { - l.mu.Lock() - defer l.mu.Unlock() - l.lazyInit() - var array []T if err := json.UnmarshalUseNumber(b, &array); err != nil { return err @@ -571,9 +567,6 @@ func (l *TList[T]) UnmarshalJSON(b []byte) error { // UnmarshalValue is an interface implement which sets any type of value for list. func (l *TList[T]) UnmarshalValue(value any) (err error) { - l.mu.Lock() - defer l.mu.Unlock() - l.lazyInit() var array []T switch value.(type) { case string, []byte: From 5cf9364c26c1f53b1c5ab4ec72163df000723da6 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 11:01:53 +0800 Subject: [PATCH 08/22] improved example --- container/glist/glist_z_example_t_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/glist/glist_z_example_t_test.go b/container/glist/glist_z_example_t_test.go index 8654756b8b4..178819fdb22 100644 --- a/container/glist/glist_z_example_t_test.go +++ b/container/glist/glist_z_example_t_test.go @@ -679,7 +679,7 @@ func ExampleTList_LockFunc() { } func ExampleTList_Join() { - var l glist.List + var l glist.TList[any] l.PushBacks(g.Slice{"a", "b", "c", "d"}) fmt.Println(l.Join(",")) From c272664faf1d743c5c270ace4a2d6c0dcf8c8f71 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 11:03:46 +0800 Subject: [PATCH 09/22] improved --- container/glist/glist_t.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index e025d68c729..ed8fc3f3cdc 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -530,9 +530,9 @@ func (l *TList[T]) Join(glue string) string { l.lazyInit() buffer := bytes.NewBuffer(nil) - length := l.Len() + length := l.len if length > 0 { - for i, e := 0, l.Front(); i < length; i, e = i+1, e.Next() { + for i, e := 0, l.front(); i < length; i, e = i+1, e.Next() { buffer.WriteString(gconv.String(e.Value)) if i != length-1 { buffer.WriteString(glue) From 16fa547181a3439e66473daf6526c63d0c90106e Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 11:05:09 +0800 Subject: [PATCH 10/22] improved --- container/glist/glist_t.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index ed8fc3f3cdc..18d6024c781 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -351,7 +351,7 @@ func (l *TList[T]) PushBackList(other *TList[T]) { l.lazyInit() - for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { + for i, e := other.len, other.front(); i > 0; i, e = i-1, e.Next() { l.insertValue(e.Value, l.root.prev) } } @@ -368,7 +368,7 @@ func (l *TList[T]) PushFrontList(other *TList[T]) { l.lazyInit() - for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { + for i, e := other.len, other.back(); i > 0; i, e = i-1, e.Prev() { l.insertValue(e.Value, &l.root) } } From 6de6d5adbb5035aac7048fb0475f536c43d342f2 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 11:08:23 +0800 Subject: [PATCH 11/22] fix bug --- container/glist/glist_t.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 18d6024c781..002904d9181 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -438,7 +438,7 @@ func (l *TList[T]) Clear() { // ToList converts TList[T] to list.List func (l *TList[T]) ToList() *list.List { nl := list.New() - for e, p := l.Front(), l.Back(); e != p; e = e.Next() { + for e := l.Front(); e != nil; e = e.Next() { nl.PushBack(e.Value) } return nl @@ -450,7 +450,7 @@ func (l *TList[T]) AppendList(nl *list.List) { if nl.Len() == 0 { return } - for e, p := nl.Front(), nl.Back(); e != p; e = e.Next() { + for e := nl.Front(); e != nil; e = e.Next() { v, _ := e.Value.(T) l.PushBack(v) } From b4f5c4008b21b6016f38e46fcd7099dd6ac86fb3 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 11:15:19 +0800 Subject: [PATCH 12/22] improved --- container/glist/glist_t.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 002904d9181..a94e8239d23 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -437,8 +437,14 @@ func (l *TList[T]) Clear() { // ToList converts TList[T] to list.List func (l *TList[T]) ToList() *list.List { + l.mu.RLock() + defer l.mu.RUnlock() + + l.lazyInit() + nl := list.New() - for e := l.Front(); e != nil; e = e.Next() { + + for e := l.front(); e != nil; e = e.Next() { nl.PushBack(e.Value) } return nl @@ -446,10 +452,15 @@ func (l *TList[T]) ToList() *list.List { // AppendList append list.List to the end func (l *TList[T]) AppendList(nl *list.List) { - l.lazyInit() if nl.Len() == 0 { return } + + l.mu.Lock() + defer l.mu.Unlock() + + l.lazyInit() + for e := nl.Front(); e != nil; e = e.Next() { v, _ := e.Value.(T) l.PushBack(v) @@ -459,13 +470,16 @@ func (l *TList[T]) AppendList(nl *list.List) { // AssignList assigns list.List to now TList[T]. // It will clear TList[T] first, and append the list.List. func (l *TList[T]) AssignList(nl *list.List) { + l.mu.Lock() + defer l.mu.Unlock() + l.init() if nl.Len() == 0 { return } - for e, p := nl.Front(), nl.Back(); e != p; e = e.Next() { + for e := nl.Front(); e != nil; e = e.Next() { v, _ := e.Value.(T) - l.PushBack(v) + l.insertValue(v, l.root.prev) } } From 09f44b8bc62a29590db48470b2af3515e04362b6 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 11:20:14 +0800 Subject: [PATCH 13/22] improved codes --- container/glist/glist_t.go | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index a94e8239d23..b22f05299f9 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -440,6 +440,11 @@ func (l *TList[T]) ToList() *list.List { l.mu.RLock() defer l.mu.RUnlock() + return l.toList() +} + +// toList converts TList[T] to list.List +func (l *TList[T]) toList() *list.List { l.lazyInit() nl := list.New() @@ -459,6 +464,15 @@ func (l *TList[T]) AppendList(nl *list.List) { l.mu.Lock() defer l.mu.Unlock() + l.appendList(nl) +} + +// appendList append list.List to the end +func (l *TList[T]) appendList(nl *list.List) { + if nl.Len() == 0 { + return + } + l.lazyInit() for e := nl.Front(); e != nil; e = e.Next() { @@ -473,6 +487,12 @@ func (l *TList[T]) AssignList(nl *list.List) { l.mu.Lock() defer l.mu.Unlock() + l.assignList(nl) +} + +// assignList assigns list.List to now TList[T]. +// It will clear TList[T] first, and append the list.List. +func (l *TList[T]) assignList(nl *list.List) { l.init() if nl.Len() == 0 { return @@ -488,16 +508,17 @@ func (l *TList[T]) RLockFunc(f func(list *list.List)) { l.mu.RLock() defer l.mu.RUnlock() - f(l.ToList()) + f(l.toList()) } // LockFunc locks writing with given callback function `f` within RWMutex.Lock. func (l *TList[T]) LockFunc(f func(list *list.List)) { l.mu.Lock() defer l.mu.Unlock() - nl := l.ToList() + + nl := l.toList() f(nl) - l.AssignList(nl) + l.assignList(nl) } // Iterator is alias of IteratorAsc. From 0d899a564d220e11f7ed4fb8ce08e0666aa1f249 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 11:21:32 +0800 Subject: [PATCH 14/22] improved comment --- container/glist/glist_t.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index b22f05299f9..fdcc067c549 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -51,7 +51,7 @@ func (e *TElement[T]) Prev() *TElement[T] { return nil } -// List is a doubly linked list containing a concurrent-safe/unsafe switch. +// TList is a doubly linked list containing a concurrent-safe/unsafe switch. // The switch should be set when its initialization and cannot be changed then. type TList[T any] struct { @@ -60,7 +60,7 @@ type TList[T any] struct { len int // current list length excluding (this) sentinel element } -// New creates and returns a new empty doubly linked list. +// NewT creates and returns a new empty doubly linked list. func NewT[T any](safe ...bool) *TList[T] { l := &TList[T]{ mu: rwmutex.Create(safe...), @@ -68,7 +68,7 @@ func NewT[T any](safe ...bool) *TList[T] { return l.init() } -// NewFrom creates and returns a list from a copy of given slice `array`. +// NewTFrom creates and returns a list from a copy of given slice `array`. // The parameter `safe` is used to specify whether using list in concurrent-safety, // which is false in default. func NewTFrom[T any](array []T, safe ...bool) *TList[T] { From e5a9d4a2d6e03320c1b003f598be1b9d5ab1b881 Mon Sep 17 00:00:00 2001 From: Hunk Zhu <54zhua@gmail.com> Date: Sat, 18 Oct 2025 11:22:48 +0800 Subject: [PATCH 15/22] Update container/glist/glist_t.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- container/glist/glist_t.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index fdcc067c549..04288dda6b7 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -1,7 +1,7 @@ // Copyright GoFrame Author(https://goframe.org). 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 l file, +// If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. // From 04e9075b8f0ca3d6f88b4bf6ad61841d276760e5 Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 13:04:00 +0800 Subject: [PATCH 16/22] improved --- container/glist/glist_t.go | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 04288dda6b7..67a572620e9 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -221,7 +221,7 @@ func (l *TList[T]) BackAll() (values []T) { return } -// FrontValue returns value of the first element of `l` or nil if the list is empty. +// FrontValue returns value of the first element of `l` or zero value of T if the list is empty. func (l *TList[T]) FrontValue() (value T) { l.mu.RLock() defer l.mu.RUnlock() @@ -232,7 +232,7 @@ func (l *TList[T]) FrontValue() (value T) { return } -// BackValue returns value of the last element of `l` or nil if the list is empty. +// BackValue returns value of the last element of `l` or zero value of T if the list is empty. func (l *TList[T]) BackValue() (value T) { l.mu.RLock() defer l.mu.RUnlock() @@ -476,8 +476,9 @@ func (l *TList[T]) appendList(nl *list.List) { l.lazyInit() for e := nl.Front(); e != nil; e = e.Next() { - v, _ := e.Value.(T) - l.PushBack(v) + if v, ok := e.Value.(T); ok { + l.insertValue(v, l.root.prev) + } } } @@ -498,8 +499,9 @@ func (l *TList[T]) assignList(nl *list.List) { return } for e := nl.Front(); e != nil; e = e.Next() { - v, _ := e.Value.(T) - l.insertValue(v, l.root.prev) + if v, ok := e.Value.(T); ok { + l.insertValue(v, l.root.prev) + } } } @@ -534,7 +536,7 @@ func (l *TList[T]) IteratorAsc(f func(e *TElement[T]) bool) { l.lazyInit() length := l.len if length > 0 { - for i, e := 0, l.Front(); i < length; i, e = i+1, e.Next() { + for i, e := 0, l.front(); i < length; i, e = i+1, e.Next() { if !f(e) { break } @@ -550,7 +552,7 @@ func (l *TList[T]) IteratorDesc(f func(e *TElement[T]) bool) { l.lazyInit() length := l.len if length > 0 { - for i, e := 0, l.Back(); i < length; i, e = i+1, e.Prev() { + for i, e := 0, l.back(); i < length; i, e = i+1, e.Prev() { if !f(e) { break } @@ -628,11 +630,11 @@ func (l *TList[T]) DeepCopy() any { l.lazyInit() var ( - length = l.Len() + length = l.len valuesT = make([]T, length) ) if length > 0 { - for i, e := 0, l.Front(); i < length; i, e = i+1, e.Next() { + for i, e := 0, l.front(); i < length; i, e = i+1, e.Next() { valuesT[i] = deepcopy.Copy(e.Value).(T) } } @@ -699,7 +701,7 @@ func (l *TList[T]) move(e, at *TElement[T]) { e.next.prev = e } -// Front returns the first element of list l or nil if the list is empty. +// front returns the first element of list l or nil if the list is empty. func (l *TList[T]) front() *TElement[T] { if l.len == 0 { return nil @@ -707,7 +709,7 @@ func (l *TList[T]) front() *TElement[T] { return l.root.next } -// Back returns the last element of list l or nil if the list is empty. +// back returns the last element of list l or nil if the list is empty. func (l *TList[T]) back() *TElement[T] { if l.len == 0 { return nil From 7206dbb173676d3689ab6d477fa3e3ea614cd969 Mon Sep 17 00:00:00 2001 From: Hunk Zhu <54zhua@gmail.com> Date: Sat, 18 Oct 2025 13:28:03 +0800 Subject: [PATCH 17/22] Update container/glist/glist_t.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- container/glist/glist_t.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 67a572620e9..4d7610b2641 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -484,25 +484,32 @@ func (l *TList[T]) appendList(nl *list.List) { // AssignList assigns list.List to now TList[T]. // It will clear TList[T] first, and append the list.List. -func (l *TList[T]) AssignList(nl *list.List) { +// Note: Elements in nl that are not assignable to T are silently skipped. +// Returns the number of skipped (incompatible) elements. +func (l *TList[T]) AssignList(nl *list.List) int { l.mu.Lock() defer l.mu.Unlock() - l.assignList(nl) + return l.assignList(nl) } // assignList assigns list.List to now TList[T]. // It will clear TList[T] first, and append the list.List. -func (l *TList[T]) assignList(nl *list.List) { +// Returns the number of skipped (incompatible) elements. +func (l *TList[T]) assignList(nl *list.List) int { l.init() if nl.Len() == 0 { - return + return 0 } + skipped := 0 for e := nl.Front(); e != nil; e = e.Next() { if v, ok := e.Value.(T); ok { l.insertValue(v, l.root.prev) + } else { + skipped++ } } + return skipped } // RLockFunc locks reading with given callback function `f` within RWMutex.RLock. From 14299edaa6d39645fb1e0d13795d88c380c1bdcc Mon Sep 17 00:00:00 2001 From: Hunk Date: Sat, 18 Oct 2025 15:20:34 +0800 Subject: [PATCH 18/22] fix bug --- container/glist/glist_t.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 4d7610b2641..141992fa49d 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -605,6 +605,7 @@ func (l *TList[T]) UnmarshalJSON(b []byte) error { if err := json.UnmarshalUseNumber(b, &array); err != nil { return err } + l.init() l.PushBacks(array) return nil } @@ -621,6 +622,7 @@ func (l *TList[T]) UnmarshalValue(value any) (err error) { return } } + l.init() l.PushBacks(array) return err } From 5fdea08d8afb16d5f0165c194651c86b6091f235 Mon Sep 17 00:00:00 2001 From: Hunk Date: Wed, 22 Oct 2025 22:05:28 +0800 Subject: [PATCH 19/22] remove some package comment. --- container/glist/glist_t.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 141992fa49d..592fb9d14f0 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -5,8 +5,6 @@ // You can obtain one at https://github.com/gogf/gf. // -// Package glist provides most commonly used doubly linked list container which also supports -// concurrent-safe/unsafe switch feature. package glist import ( From c3fae3c8668082e18bc0128e35682c7754e217aa Mon Sep 17 00:00:00 2001 From: Hunk Date: Wed, 22 Oct 2025 23:20:36 +0800 Subject: [PATCH 20/22] improve --- container/glist/glist_t.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 592fb9d14f0..6bbc0031ffe 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -593,7 +593,7 @@ func (l *TList[T]) String() string { } // MarshalJSON implements the interface MarshalJSON for json.Marshal. -func (l TList[T]) MarshalJSON() ([]byte, error) { +func (l *TList[T]) MarshalJSON() ([]byte, error) { return json.Marshal(l.FrontAll()) } From fac38a4eae84eb55c9325856ce95541bb2ee808a Mon Sep 17 00:00:00 2001 From: Hunk <54zhua@gmail.com> Date: Thu, 23 Oct 2025 11:14:11 +0800 Subject: [PATCH 21/22] Revert "improve" This reverts commit c3fae3c8668082e18bc0128e35682c7754e217aa. --- container/glist/glist_t.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 6bbc0031ffe..592fb9d14f0 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -593,7 +593,7 @@ func (l *TList[T]) String() string { } // MarshalJSON implements the interface MarshalJSON for json.Marshal. -func (l *TList[T]) MarshalJSON() ([]byte, error) { +func (l TList[T]) MarshalJSON() ([]byte, error) { return json.Marshal(l.FrontAll()) } From e540c44eaa7724bc12400ab6f2441c76ae2c322f Mon Sep 17 00:00:00 2001 From: hailaz <739476267@qq.com> Date: Thu, 20 Nov 2025 09:46:46 +0000 Subject: [PATCH 22/22] feat(container/glist): add tests for ToList and AppendList methods --- container/glist/glist_t.go | 4 - container/glist/glist_z_unit_t_test.go | 172 +++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 4 deletions(-) diff --git a/container/glist/glist_t.go b/container/glist/glist_t.go index 592fb9d14f0..4f23d2b7877 100644 --- a/container/glist/glist_t.go +++ b/container/glist/glist_t.go @@ -455,10 +455,6 @@ func (l *TList[T]) toList() *list.List { // AppendList append list.List to the end func (l *TList[T]) AppendList(nl *list.List) { - if nl.Len() == 0 { - return - } - l.mu.Lock() defer l.mu.Unlock() diff --git a/container/glist/glist_z_unit_t_test.go b/container/glist/glist_z_unit_t_test.go index fcfb1fc3556..8a431061164 100644 --- a/container/glist/glist_z_unit_t_test.go +++ b/container/glist/glist_z_unit_t_test.go @@ -7,6 +7,7 @@ package glist import ( + "container/list" "testing" "github.com/gogf/gf/v2/internal/json" @@ -758,4 +759,175 @@ func TestTList_DeepCopy(t *testing.T) { cl.PopBack() t.AssertNE(l.Size(), cl.Size()) }) + // Nil pointer deep copy + gtest.C(t, func(t *gtest.T) { + var l *TList[any] + copyList := l.DeepCopy() + t.AssertNil(copyList) + }) +} + +func TestTList_ToList(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewTFrom([]any{1, 2, 3, 4, 5}) + nl := l.ToList() + t.Assert(nl.Len(), 5) + + // Verify elements + i := 1 + for e := nl.Front(); e != nil; e = e.Next() { + t.Assert(e.Value, i) + i++ + } + }) + // Empty list + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + nl := l.ToList() + t.Assert(nl.Len(), 0) + }) +} + +func TestTList_AppendList(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewTFrom([]any{1, 2, 3}) + nl := list.New() + nl.PushBack(4) + nl.PushBack(5) + + l.AppendList(nl) + t.Assert(l.Len(), 5) + t.Assert(l.FrontAll(), []any{1, 2, 3, 4, 5}) + }) + // Append empty list + gtest.C(t, func(t *gtest.T) { + l := NewTFrom([]any{1, 2, 3}) + nl := list.New() + l.AppendList(nl) + t.Assert(l.Len(), 3) + t.Assert(l.FrontAll(), []any{1, 2, 3}) + }) + // Append with type mismatch (should skip) + gtest.C(t, func(t *gtest.T) { + l := NewT[int]() + nl := list.New() + nl.PushBack(1) + nl.PushBack("string") // type mismatch + nl.PushBack(2) + + l.AppendList(nl) + t.Assert(l.Len(), 2) + t.Assert(l.FrontAll(), []int{1, 2}) + }) +} + +func TestTList_AssignList(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewTFrom([]any{1, 2, 3}) + nl := list.New() + nl.PushBack(4) + nl.PushBack(5) + nl.PushBack(6) + + skipped := l.AssignList(nl) + t.Assert(skipped, 0) + t.Assert(l.Len(), 3) + t.Assert(l.FrontAll(), []any{4, 5, 6}) + }) + // Assign empty list + gtest.C(t, func(t *gtest.T) { + l := NewTFrom([]any{1, 2, 3}) + nl := list.New() + + skipped := l.AssignList(nl) + t.Assert(skipped, 0) + t.Assert(l.Len(), 0) + }) + // Assign with type mismatch (should return skipped count) + gtest.C(t, func(t *gtest.T) { + l := NewT[int]() + nl := list.New() + nl.PushBack(1) + nl.PushBack("string") // type mismatch + nl.PushBack(2) + nl.PushBack("another") // type mismatch + + skipped := l.AssignList(nl) + t.Assert(skipped, 2) + t.Assert(l.Len(), 2) + t.Assert(l.FrontAll(), []int{1, 2}) + }) +} + +func TestTList_String_Nil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + var l *TList[any] + t.Assert(l.String(), "") + }) +} + +func TestTList_UnmarshalJSON_Error(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + err := l.UnmarshalJSON([]byte("invalid json")) + t.AssertNE(err, nil) + }) +} + +func TestTList_UnmarshalValue_String(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + err := l.UnmarshalValue(`[1,2,3]`) + t.AssertNil(err) + t.Assert(l.FrontAll(), []any{1, 2, 3}) + }) +} + +func TestTList_UnmarshalValue_Bytes(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + err := l.UnmarshalValue([]byte(`[1,2,3]`)) + t.AssertNil(err) + t.Assert(l.FrontAll(), []any{1, 2, 3}) + }) +} + +func TestTList_DeepCopy_Empty(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + copyList := l.DeepCopy() + cl := copyList.(*TList[any]) + t.Assert(cl.Len(), 0) + }) +} + +func TestTList_AppendList_WithTypeMismatch(t *testing.T) { + // Test appendList internal function through AppendList with mixed types + gtest.C(t, func(t *gtest.T) { + l := NewT[int]() + nl := list.New() + // Only add non-matching types + nl.PushBack("string1") + nl.PushBack("string2") + + l.AppendList(nl) + t.Assert(l.Len(), 0) + }) +} + +func TestTList_UnmarshalValue_Error(t *testing.T) { + // Test UnmarshalValue with data through default case + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + // Pass a slice directly through default case + _ = l.UnmarshalValue([]any{1, 2, 3}) + t.Assert(l.Len(), 3) + t.Assert(l.FrontAll(), []any{1, 2, 3}) + }) + // Test UnmarshalValue error in string case + gtest.C(t, func(t *gtest.T) { + l := NewT[any]() + err := l.UnmarshalValue("invalid json") + t.AssertNE(err, nil) + }) }