Skip to content

Commit 0871562

Browse files
committed
feat: implement MarshalJSON for all queues
1 parent 9c6ece2 commit 0871562

9 files changed

+195
-1
lines changed

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ repos:
44
hooks:
55
- id: commitizen
66
- repo: https://github.com/golangci/golangci-lint
7-
rev: v1.63.3
7+
rev: v1.63.4
88
hooks:
99
- id: golangci-lint
1010
name: golangci-lint

blocking.go

+25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package queue
22

33
import (
4+
"encoding/json"
5+
"fmt"
46
"sync"
57
)
68

@@ -295,3 +297,26 @@ func (bq *Blocking[T]) get() (v T, _ error) {
295297

296298
return elem, nil
297299
}
300+
301+
// MarshalJSON serializes the Blocking queue to JSON.
302+
func (bq *Blocking[T]) MarshalJSON() ([]byte, error) {
303+
bq.lock.RLock()
304+
305+
if bq.IsEmpty() {
306+
bq.lock.RUnlock()
307+
return []byte("[]"), nil
308+
}
309+
310+
// Extract elements from `elements` starting at `elementsIndex`.
311+
elements := bq.elements[bq.elementsIndex:]
312+
313+
bq.lock.RUnlock()
314+
315+
// Marshal the slice of elements into JSON.
316+
data, err := json.Marshal(elements)
317+
if err != nil {
318+
return nil, fmt.Errorf("failed to marshal blocking queue: %w", err)
319+
}
320+
321+
return data, nil
322+
}

blocking_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package queue_test
22

33
import (
4+
"bytes"
5+
"encoding/json"
46
"errors"
57
"fmt"
68
"reflect"
@@ -704,6 +706,24 @@ func TestBlocking(t *testing.T) {
704706
}
705707
})
706708
})
709+
710+
t.Run("MarshalJSON", func(t *testing.T) {
711+
t.Parallel()
712+
713+
elems := []int{3, 2, 1}
714+
715+
q := queue.NewBlocking(elems)
716+
717+
marshaled, err := json.Marshal(q)
718+
if err != nil {
719+
t.Fatalf("expected no error, got %v", err)
720+
}
721+
722+
expectedMarshaled := []byte(`[3,2,1]`)
723+
if !bytes.Equal(expectedMarshaled, marshaled) {
724+
t.Fatalf("expected marshaled to be %s, got %s", expectedMarshaled, marshaled)
725+
}
726+
})
707727
}
708728

709729
func testResetOnMultipleRoutinesFunc[T comparable](

circular.go

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package queue
22

33
import (
4+
"encoding/json"
45
"sync"
56
)
67

@@ -231,3 +232,25 @@ func (q *Circular[T]) get() (v T, _ error) {
231232
func (q *Circular[T]) isEmpty() bool {
232233
return q.size == 0
233234
}
235+
236+
// MarshalJSON serializes the Circular queue to JSON.
237+
func (q *Circular[T]) MarshalJSON() ([]byte, error) {
238+
q.lock.RLock()
239+
240+
if q.isEmpty() {
241+
q.lock.RUnlock()
242+
return []byte("[]"), nil
243+
}
244+
245+
// Collect elements in logical order from head to tail.
246+
elements := make([]T, 0, q.size)
247+
248+
for i := 0; i < q.size; i++ {
249+
index := (q.head + i) % len(q.elems)
250+
elements = append(elements, q.elems[index])
251+
}
252+
253+
q.lock.RUnlock()
254+
255+
return json.Marshal(elements)
256+
}

circular_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package queue_test
22

33
import (
4+
"bytes"
5+
"encoding/json"
46
"errors"
57
"reflect"
68
"testing"
@@ -290,6 +292,24 @@ func TestCircular(t *testing.T) {
290292
t.Fatalf("expected elements to be %v, got %v", elems, iterElems)
291293
}
292294
})
295+
296+
t.Run("MarshalJSON", func(t *testing.T) {
297+
t.Parallel()
298+
299+
elems := []int{3, 2, 1}
300+
301+
q := queue.NewCircular(elems, 4)
302+
303+
marshaled, err := json.Marshal(q)
304+
if err != nil {
305+
t.Fatalf("expected no error, got %v", err)
306+
}
307+
308+
expectedMarshaled := []byte(`[3,2,1]`)
309+
if !bytes.Equal(expectedMarshaled, marshaled) {
310+
t.Fatalf("expected marshaled to be %s, got %s", expectedMarshaled, marshaled)
311+
}
312+
})
293313
}
294314

295315
func BenchmarkCircularQueue(b *testing.B) {

linked.go

+20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package queue
22

33
import (
4+
"encoding/json"
45
"sync"
56
)
67

@@ -189,3 +190,22 @@ func (lq *Linked[T]) Clear() []T {
189190

190191
return elements
191192
}
193+
194+
// MarshalJSON serializes the Linked queue to JSON.
195+
func (lq *Linked[T]) MarshalJSON() ([]byte, error) {
196+
lq.lock.RLock()
197+
defer lq.lock.RUnlock()
198+
199+
// Traverse the linked list and collect elements.
200+
elements := make([]T, 0, lq.size)
201+
202+
current := lq.head
203+
204+
for current != nil {
205+
elements = append(elements, current.value)
206+
current = current.next
207+
}
208+
209+
// Marshal the elements slice to JSON.
210+
return json.Marshal(elements)
211+
}

linked_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package queue_test
22

33
import (
4+
"bytes"
5+
"encoding/json"
46
"errors"
57
"reflect"
68
"testing"
@@ -231,6 +233,24 @@ func TestLinked(t *testing.T) {
231233
t.Fatalf("expected elements to be %v, got %v", elems, iterElems)
232234
}
233235
})
236+
237+
t.Run("MarshalJSON", func(t *testing.T) {
238+
t.Parallel()
239+
240+
elems := []int{3, 2, 1}
241+
242+
q := queue.NewLinked(elems)
243+
244+
marshaled, err := json.Marshal(q)
245+
if err != nil {
246+
t.Fatalf("expected no error, got %v", err)
247+
}
248+
249+
expectedMarshaled := []byte(`[3,2,1]`)
250+
if !bytes.Equal(expectedMarshaled, marshaled) {
251+
t.Fatalf("expected marshaled to be %s, got %s", expectedMarshaled, marshaled)
252+
}
253+
})
234254
}
235255

236256
func BenchmarkLinkedQueue(b *testing.B) {

priority.go

+30
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package queue
22

33
import (
44
"container/heap"
5+
"encoding/json"
56
"sort"
67
"sync"
78
)
@@ -269,3 +270,32 @@ func (pq *Priority[T]) Size() int {
269270

270271
return pq.elements.Len()
271272
}
273+
274+
// MarshalJSON serializes the Priority queue to JSON.
275+
func (pq *Priority[T]) MarshalJSON() ([]byte, error) {
276+
pq.lock.RLock()
277+
278+
// Create a temporary copy of the heap to extract elements in order.
279+
tempHeap := &priorityHeap[T]{
280+
elems: make([]T, len(pq.elements.elems)),
281+
lessFunc: pq.elements.lessFunc,
282+
}
283+
284+
copy(tempHeap.elems, pq.elements.elems)
285+
286+
pq.lock.RUnlock()
287+
288+
heap.Init(tempHeap)
289+
290+
output := make([]T, len(tempHeap.elems))
291+
292+
i := 0
293+
294+
for tempHeap.Len() > 0 {
295+
// nolint: forcetypeassert, revive
296+
output[i] = tempHeap.Pop().(T)
297+
i++
298+
}
299+
300+
return json.Marshal(output)
301+
}

priority_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package queue_test
22

33
import (
4+
"bytes"
5+
"encoding/json"
46
"errors"
57
"reflect"
68
"sort"
@@ -362,6 +364,24 @@ func TestPriority(t *testing.T) {
362364
}
363365
})
364366
})
367+
368+
t.Run("MarshalJSON", func(t *testing.T) {
369+
t.Parallel()
370+
371+
elems := []int{3, 2, 1}
372+
373+
priorityQueue := queue.NewPriority(elems, lessAscending)
374+
375+
marshaled, err := json.Marshal(priorityQueue)
376+
if err != nil {
377+
t.Fatalf("expected no error, got %v", err)
378+
}
379+
380+
expectedMarshaled := []byte(`[3,2,1]`)
381+
if !bytes.Equal(expectedMarshaled, marshaled) {
382+
t.Fatalf("expected marshaled to be %s, got %s", expectedMarshaled, marshaled)
383+
}
384+
})
365385
}
366386

367387
func FuzzPriority(f *testing.F) {
@@ -411,6 +431,22 @@ func FuzzPriority(f *testing.F) {
411431
}
412432

413433
func BenchmarkPriorityQueue(b *testing.B) {
434+
b.Run("MarshalJSON", func(b *testing.B) {
435+
priorityQueue := queue.NewPriority[int](
436+
[]int{2, 3, 1, 5, 4},
437+
func(elem, otherElem int) bool {
438+
return elem < otherElem
439+
},
440+
)
441+
442+
b.ReportAllocs()
443+
b.ResetTimer()
444+
445+
for i := 0; i <= b.N; i++ {
446+
_, _ = priorityQueue.MarshalJSON()
447+
}
448+
})
449+
414450
b.Run("Peek", func(b *testing.B) {
415451
priorityQueue := queue.NewPriority([]int{1}, func(elem, otherElem int) bool {
416452
return elem < otherElem

0 commit comments

Comments
 (0)