protobook.go 3.33 KB
Newer Older
1 2 3 4 5
package pstoremem

import (
	"sync"

tavit ohanian's avatar
tavit ohanian committed
6
	peer "gitlab.dms3.io/p2p/go-p2p-core/peer"
7

tavit ohanian's avatar
tavit ohanian committed
8
	pstore "gitlab.dms3.io/p2p/go-p2p-core/peerstore"
9 10 11
)

type protoSegment struct {
vyzo's avatar
vyzo committed
12
	sync.RWMutex
13 14 15 16 17
	protocols map[peer.ID]map[string]struct{}
}

type protoSegments [256]*protoSegment

vyzo's avatar
vyzo committed
18
func (s *protoSegments) get(p peer.ID) *protoSegment {
19
	return s[byte(p[len(p)-1])]
20 21 22 23
}

type memoryProtoBook struct {
	segments protoSegments
vyzo's avatar
vyzo committed
24 25 26

	lk       sync.RWMutex
	interned map[string]string
27 28 29 30
}

var _ pstore.ProtoBook = (*memoryProtoBook)(nil)

Yusef Napora's avatar
Yusef Napora committed
31
func NewProtoBook() *memoryProtoBook {
32
	return &memoryProtoBook{
33
		interned: make(map[string]string, 256),
34 35 36 37 38 39 40 41 42 43 44
		segments: func() (ret protoSegments) {
			for i := range ret {
				ret[i] = &protoSegment{
					protocols: make(map[peer.ID]map[string]struct{}),
				}
			}
			return ret
		}(),
	}
}

vyzo's avatar
vyzo committed
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
func (pb *memoryProtoBook) internProtocol(proto string) string {
	// check if it is interned with the read lock
	pb.lk.RLock()
	interned, ok := pb.interned[proto]
	pb.lk.RUnlock()

	if ok {
		return interned
	}

	// intern with the write lock
	pb.lk.Lock()
	defer pb.lk.Unlock()

	// check again in case it got interned in between locks
	interned, ok = pb.interned[proto]
	if ok {
		return interned
	}

	pb.interned[proto] = proto
	return proto
}

69
func (pb *memoryProtoBook) SetProtocols(p peer.ID, protos ...string) error {
Steven Allen's avatar
Steven Allen committed
70 71 72 73
	if err := p.Validate(); err != nil {
		return err
	}

74
	s := pb.segments.get(p)
vyzo's avatar
vyzo committed
75 76
	s.Lock()
	defer s.Unlock()
77 78 79

	newprotos := make(map[string]struct{}, len(protos))
	for _, proto := range protos {
vyzo's avatar
vyzo committed
80
		newprotos[pb.internProtocol(proto)] = struct{}{}
81 82 83 84 85 86 87 88
	}

	s.protocols[p] = newprotos

	return nil
}

func (pb *memoryProtoBook) AddProtocols(p peer.ID, protos ...string) error {
Steven Allen's avatar
Steven Allen committed
89 90 91 92
	if err := p.Validate(); err != nil {
		return err
	}

93
	s := pb.segments.get(p)
vyzo's avatar
vyzo committed
94 95
	s.Lock()
	defer s.Unlock()
96 97 98 99 100 101 102 103

	protomap, ok := s.protocols[p]
	if !ok {
		protomap = make(map[string]struct{})
		s.protocols[p] = protomap
	}

	for _, proto := range protos {
vyzo's avatar
vyzo committed
104
		protomap[pb.internProtocol(proto)] = struct{}{}
105 106 107 108 109 110
	}

	return nil
}

func (pb *memoryProtoBook) GetProtocols(p peer.ID) ([]string, error) {
Steven Allen's avatar
Steven Allen committed
111 112 113 114
	if err := p.Validate(); err != nil {
		return nil, err
	}

115
	s := pb.segments.get(p)
vyzo's avatar
vyzo committed
116 117
	s.RLock()
	defer s.RUnlock()
118 119 120 121 122 123 124 125 126

	out := make([]string, 0, len(s.protocols))
	for k := range s.protocols[p] {
		out = append(out, k)
	}

	return out, nil
}

127
func (pb *memoryProtoBook) RemoveProtocols(p peer.ID, protos ...string) error {
Steven Allen's avatar
Steven Allen committed
128 129 130 131
	if err := p.Validate(); err != nil {
		return err
	}

132
	s := pb.segments.get(p)
133 134
	s.Lock()
	defer s.Unlock()
135 136 137 138 139 140 141 142 143 144 145 146 147

	protomap, ok := s.protocols[p]
	if !ok {
		// nothing to remove.
		return nil
	}

	for _, proto := range protos {
		delete(protomap, pb.internProtocol(proto))
	}
	return nil
}

148
func (pb *memoryProtoBook) SupportsProtocols(p peer.ID, protos ...string) ([]string, error) {
Steven Allen's avatar
Steven Allen committed
149 150 151 152
	if err := p.Validate(); err != nil {
		return nil, err
	}

153
	s := pb.segments.get(p)
vyzo's avatar
vyzo committed
154 155
	s.RLock()
	defer s.RUnlock()
156 157 158 159 160 161 162 163 164 165

	out := make([]string, 0, len(protos))
	for _, proto := range protos {
		if _, ok := s.protocols[p][proto]; ok {
			out = append(out, proto)
		}
	}

	return out, nil
}
Aarsh Shah's avatar
Aarsh Shah committed
166

Aarsh Shah's avatar
Aarsh Shah committed
167
func (pb *memoryProtoBook) FirstSupportedProtocol(p peer.ID, protos ...string) (string, error) {
Aarsh Shah's avatar
Aarsh Shah committed
168
	if err := p.Validate(); err != nil {
Aarsh Shah's avatar
Aarsh Shah committed
169
		return "", err
Aarsh Shah's avatar
Aarsh Shah committed
170 171 172 173 174 175 176 177
	}

	s := pb.segments.get(p)
	s.RLock()
	defer s.RUnlock()

	for _, proto := range protos {
		if _, ok := s.protocols[p][proto]; ok {
Aarsh Shah's avatar
Aarsh Shah committed
178
			return proto, nil
Aarsh Shah's avatar
Aarsh Shah committed
179 180
		}
	}
Aarsh Shah's avatar
Aarsh Shah committed
181
	return "", nil
Aarsh Shah's avatar
Aarsh Shah committed
182
}