From 9436956e2a291a686a30ac721cd2c0fe1bb9c3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E6=89=87=E6=BB=91=E7=BF=94=E7=BF=BC?= Date: Fri, 13 Jun 2025 08:30:58 +0000 Subject: [PATCH] Add and use new TypedSyncMap --- common/utils/TypedSyncMap.go | 59 ++++++++++++++++++++++++++++++++++++ proxy/freedom/freedom.go | 9 +++--- 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 common/utils/TypedSyncMap.go diff --git a/common/utils/TypedSyncMap.go b/common/utils/TypedSyncMap.go new file mode 100644 index 00000000..7512c0fb --- /dev/null +++ b/common/utils/TypedSyncMap.go @@ -0,0 +1,59 @@ +package util + +import ( + "sync" +) + +// TypedSyncMap is a wrapper of sync.Map that provides type-safe for keys and values. +// No need to use type assertions every time, so you can have more time to enjoy other things like GochiUsa +type TypedSyncMap[K, V any] struct { + syncMap *sync.Map +} + +func NewTypedSyncMap[K any, V any]() *TypedSyncMap[K, V] { + return &TypedSyncMap[K, V]{ + syncMap: &sync.Map{}, + } +} + +func (m *TypedSyncMap[K, V]) Clear() { + m.syncMap.Clear() +} + +func (m *TypedSyncMap[K, V]) CompareAndDelete(key K, old V) (deleted bool) { + return m.syncMap.CompareAndDelete(key, old) +} + +func (m *TypedSyncMap[K, V]) CompareAndSwap(key K, old V, new V) (swapped bool) { + return m.syncMap.CompareAndSwap(key, old, new) +} + +func (m *TypedSyncMap[K, V]) Delete(key K) { + m.syncMap.Delete(key) +} + +func (m *TypedSyncMap[K, V]) Load(key K) (value V, ok bool) { + anyValue, ok := m.syncMap.Load(key) + return anyValue.(V), ok +} + +func (m *TypedSyncMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) { + anyValue, loaded := m.syncMap.LoadAndDelete(key) + return anyValue.(V), loaded +} +func (m *TypedSyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) { + anyActual, loaded := m.syncMap.LoadOrStore(key, value) + return anyActual.(V), loaded +} +func (m *TypedSyncMap[K, V]) Range(f func(key K, value V) bool) { + m.syncMap.Range(func(key, value any) bool { + return f(key.(K), value.(V)) + }) +} +func (m *TypedSyncMap[K, V]) Store(key K, value V) { + m.syncMap.Store(key, value) +} +func (m *TypedSyncMap[K, V]) Swap(key K, value V) (previous V, loaded bool) { + anyPrevious, loaded := m.syncMap.Swap(key, value) + return anyPrevious.(V), loaded +} \ No newline at end of file diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 0399c3d1..e4f49d9d 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -27,6 +27,7 @@ import ( "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/tls" + "github.com/xtls/xray-core/common/utils" ) var useSplice bool @@ -343,7 +344,7 @@ func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride Handler: h, Context: ctx, UDPOverride: UDPOverride, - resolvedUDPAddr: resolvedUDPAddr, + resolvedUDPAddr: util.NewTypedSyncMap[string, net.Address](), } } @@ -361,7 +362,7 @@ type PacketWriter struct { // But resolver will return a random one if the domain has many IPs // Resulting in these packets being sent to many different IPs randomly // So, cache and keep the resolve result - resolvedUDPAddr map[string]net.Address + resolvedUDPAddr *util.TypedSyncMap[string, net.Address] } func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { @@ -381,13 +382,13 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error { b.UDP.Port = w.UDPOverride.Port } if w.Handler.config.hasStrategy() && b.UDP.Address.Family().IsDomain() { - if ip := w.resolvedUDPAddr[b.UDP.Address.Domain()]; ip != nil { + if ip, ok := w.resolvedUDPAddr.Load(b.UDP.Address.Domain()); ok { b.UDP.Address = ip } else { ip := w.Handler.resolveIP(w.Context, b.UDP.Address.Domain(), nil) if ip != nil { b.UDP.Address = ip - w.resolvedUDPAddr[b.UDP.Address.Domain()] = ip + w.resolvedUDPAddr.Store(b.UDP.Address.Domain(), ip) } } }