package graphimport ()// Store represents a storage for vertices and edges. The graph library provides an in-memory store// by default and accepts any Store implementation to work with - for example, an SQL store.//// When implementing your own Store, make sure the individual methods and their behavior adhere to// this documentation. Otherwise, the graphs aren't guaranteed to behave as expected.typeStore[ comparable, any] interface {// AddVertex should add the given vertex with the given hash value and vertex properties to the // graph. If the vertex already exists, it is up to you whether ErrVertexAlreadyExists or no // error should be returned.AddVertex(hash , value , properties VertexProperties) error// Vertex should return the vertex and vertex properties with the given hash value. If the // vertex doesn't exist, ErrVertexNotFound should be returned.Vertex(hash ) (, VertexProperties, error)// RemoveVertex should remove the vertex with the given hash value. If the vertex doesn't // exist, ErrVertexNotFound should be returned. If the vertex has edges to other vertices, // ErrVertexHasEdges should be returned.RemoveVertex(hash ) error// ListVertices should return all vertices in the graph in a slice.ListVertices() ([], error)// VertexCount should return the number of vertices in the graph. This should be equal to the // length of the slice returned by ListVertices.VertexCount() (int, error)// AddEdge should add an edge between the vertices with the given source and target hashes. // // If either vertex doesn't exit, ErrVertexNotFound should be returned for the respective // vertex. If the edge already exists, ErrEdgeAlreadyExists should be returned.AddEdge(sourceHash, targetHash , edge Edge[]) error// UpdateEdge should update the edge between the given vertices with the data of the given // Edge instance. If the edge doesn't exist, ErrEdgeNotFound should be returned.UpdateEdge(sourceHash, targetHash , edge Edge[]) error// RemoveEdge should remove the edge between the vertices with the given source and target // hashes. // // If either vertex doesn't exist, it is up to you whether ErrVertexNotFound or no error should // be returned. If the edge doesn't exist, it is up to you whether ErrEdgeNotFound or no error // should be returned.RemoveEdge(sourceHash, targetHash ) error// Edge should return the edge joining the vertices with the given hash values. It should // exclusively look for an edge between the source and the target vertex, not vice versa. The // graph implementation does this for undirected graphs itself. // // Note that unlike Graph.Edge, this function is supposed to return an Edge[K], i.e. an edge // that only contains the vertex hashes instead of the vertices themselves. // // If the edge doesn't exist, ErrEdgeNotFound should be returned.Edge(sourceHash, targetHash ) (Edge[], error)// ListEdges should return all edges in the graph in a slice.ListEdges() ([]Edge[], error)}type memoryStore[ comparable, any] struct { lock sync.RWMutex vertices map[] vertexProperties map[]VertexProperties// outEdges and inEdges store all outgoing and ingoing edges for all vertices. For O(1) access, // these edges themselves are stored in maps whose keys are the hashes of the target vertices. outEdges map[]map[]Edge[] // source -> target inEdges map[]map[]Edge[] // target -> source}func newMemoryStore[ comparable, any]() Store[, ] {return &memoryStore[, ]{vertices: make(map[]),vertexProperties: make(map[]VertexProperties),outEdges: make(map[]map[]Edge[]),inEdges: make(map[]map[]Edge[]), }}func ( *memoryStore[, ]) ( , , VertexProperties) error { .lock.Lock()defer .lock.Unlock()if , := .vertices[]; {returnErrVertexAlreadyExists } .vertices[] = .vertexProperties[] = returnnil}func ( *memoryStore[, ]) () ([], error) { .lock.RLock()defer .lock.RUnlock() := make([], 0, len(.vertices))for := range .vertices { = append(, ) }return , nil}func ( *memoryStore[, ]) () (int, error) { .lock.RLock()defer .lock.RUnlock()returnlen(.vertices), nil}func ( *memoryStore[, ]) ( ) (, VertexProperties, error) { .lock.RLock()defer .lock.RUnlock() , := .vertices[]if ! {return , VertexProperties{}, ErrVertexNotFound } := .vertexProperties[]return , , nil}func ( *memoryStore[, ]) ( ) error { .lock.RLock()defer .lock.RUnlock()if , := .vertices[]; ! {returnErrVertexNotFound }if , := .inEdges[]; {iflen() > 0 {returnErrVertexHasEdges }delete(.inEdges, ) }if , := .outEdges[]; {iflen() > 0 {returnErrVertexHasEdges }delete(.outEdges, ) }delete(.vertices, )delete(.vertexProperties, )returnnil}func ( *memoryStore[, ]) (, , Edge[]) error { .lock.Lock()defer .lock.Unlock()if , := .outEdges[]; ! { .outEdges[] = make(map[]Edge[]) } .outEdges[][] = if , := .inEdges[]; ! { .inEdges[] = make(map[]Edge[]) } .inEdges[][] = returnnil}func ( *memoryStore[, ]) (, , Edge[]) error {if , := .Edge(, ); != nil {return } .lock.Lock()defer .lock.Unlock() .outEdges[][] = .inEdges[][] = returnnil}func ( *memoryStore[, ]) (, ) error { .lock.Lock()defer .lock.Unlock()delete(.inEdges[], )delete(.outEdges[], )returnnil}func ( *memoryStore[, ]) (, ) (Edge[], error) { .lock.RLock()defer .lock.RUnlock() , := .outEdges[]if ! {returnEdge[]{}, ErrEdgeNotFound } , := []if ! {returnEdge[]{}, ErrEdgeNotFound }return , nil}func ( *memoryStore[, ]) () ([]Edge[], error) { .lock.RLock()defer .lock.RUnlock() := make([]Edge[], 0)for , := range .outEdges {for , := range { = append(, ) } }return , nil}// CreatesCycle is a fastpath version of [CreatesCycle] that avoids calling// [PredecessorMap], which generates large amounts of garbage to collect.//// Because CreatesCycle doesn't need to modify the PredecessorMap, we can use// inEdges instead to compute the same thing without creating any copies.func ( *memoryStore[, ]) (, ) (bool, error) {if , , := .Vertex(); != nil {returnfalse, fmt.Errorf("could not get vertex with hash %v: %w", , ) }if , , := .Vertex(); != nil {returnfalse, fmt.Errorf("could not get vertex with hash %v: %w", , ) }if == {returntrue, nil } := make([], 0) := make(map[]struct{}) = append(, )forlen() > 0 { := [len()-1] = [:len()-1]if , := []; ! {// If the adjacent vertex also is the target vertex, the target is a // parent of the source vertex. An edge would introduce a cycle.if == {returntrue, nil } [] = struct{}{}for := range .inEdges[] { = append(, ) } } }returnfalse, nil}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.