-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmap.go
More file actions
170 lines (144 loc) · 3.96 KB
/
Copy pathmap.go
File metadata and controls
170 lines (144 loc) · 3.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Package orderedmap provides a Map that preserves the order of key value pairs.
package orderedmap
import (
"bytes"
"encoding/json"
"fmt"
"sort"
)
// Map implements a map that keeps the item order.
// It preserves the insertion order of keys for both JSON unmarshalling
// and manual operations.
type Map struct {
// Data holds the map entries with their sequence indices.
Data map[string]Entry
// Keys contains the map keys in insertion order.
Keys []string
}
// Len returns the number of elements within the map.
func (m *Map) Len() int {
return len(m.Keys)
}
// Get retrieves the value for the given key.
// Returns the value and true if the key exists, or nil and false otherwise.
func (m *Map) Get(key string) (any, bool) {
if m.Data == nil {
return nil, false
}
entry, exists := m.Data[key]
if !exists {
return nil, false
}
return entry.Value, true
}
// Set adds or updates a key-value pair in the map.
// If the key already exists, its value is updated without changing position.
// If the key is new, it is appended to the end of the insertion order.
func (m *Map) Set(key string, value any) {
if m.Data == nil {
m.Data = make(map[string]Entry)
}
if _, exists := m.Data[key]; !exists {
m.Keys = append(m.Keys, key)
}
m.Data[key] = Entry{
index: nextSequence(),
Value: value,
}
}
// Delete removes a key-value pair from the map.
// Returns true if the key existed and was deleted, false otherwise.
//
// Performance note: This operation is O(n) where n is the number of keys,
// as it requires searching through the Keys slice to find and remove the key.
func (m *Map) Delete(key string) bool {
if m.Data == nil {
return false
}
if _, exists := m.Data[key]; !exists {
return false
}
delete(m.Data, key)
for i, k := range m.Keys {
if k == key {
m.Keys = append(m.Keys[:i], m.Keys[i+1:]...)
break
}
}
return true
}
// GetAndDelete retrieves and removes a key in one operation.
// Returns the value and true if the key existed, or nil and false otherwise.
//
// Performance note: This operation is O(n) due to the underlying Delete operation.
func (m *Map) GetAndDelete(key string) (value any, loaded bool) {
value, loaded = m.Get(key)
if loaded {
m.Delete(key)
}
return value, loaded
}
// Clear removes all entries from the map, resulting in an empty map.
func (m *Map) Clear() {
m.Data = make(map[string]Entry)
m.Keys = nil
}
// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration.
func (m *Map) Range(f func(key string, value any) bool) {
if m.Data == nil || len(m.Keys) == 0 {
return
}
for _, key := range m.Keys {
entry := m.Data[key]
if !f(key, entry.Value) {
return
}
}
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (m *Map) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &m.Data); err != nil {
return fmt.Errorf("failed to unmarshal orderedmap: %w", err)
}
m.rebuildKeys()
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (m *Map) MarshalJSON() ([]byte, error) {
if m == nil || len(m.Keys) == 0 {
return []byte("{}"), nil
}
buf := bytes.NewBuffer(make([]byte, 0, len(m.Keys)*20))
buf.WriteString("{")
lastIdx := len(m.Keys) - 1
for i, key := range m.Keys {
keyData, err := json.Marshal(key)
if err != nil {
return nil, fmt.Errorf("marshalling key %q: %w", key, err)
}
buf.Write(keyData)
buf.WriteByte(':')
value := m.Data[key].Value
b, err := json.Marshal(value)
if err != nil {
return nil, fmt.Errorf("marshalling entry for key %q: %w", key, err)
}
buf.Write(b)
if i < lastIdx {
buf.WriteByte(',')
}
}
buf.WriteByte('}')
return buf.Bytes(), nil
}
// rebuildKeys builds the keys slice sorted by insertion order.
func (m *Map) rebuildKeys() {
m.Keys = make([]string, 0, len(m.Data))
for name := range m.Data {
m.Keys = append(m.Keys, name)
}
sort.SliceStable(m.Keys, func(i, j int) bool {
return m.Data[m.Keys[i]].index < m.Data[m.Keys[j]].index
})
}