tools: Add src/container
Contains generic Map and Set types. Golang 1.18 added new support for generics, but has not yet added a standard library that provides generic containers. In future versions of Golang, there will almost certainly be similar implementations of these types. Until then, use these to simplify some code. 100% test coverage. Bug: dawn:1342 Change-Id: I2a5c7bfb26f15c2099037d3fa0f0576df641d9f6 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/85220 Auto-Submit: Ben Clayton <bclayton@google.com> Reviewed-by: Ryan Harrison <rharrison@chromium.org> Commit-Queue: Ryan Harrison <rharrison@chromium.org>
This commit is contained in:
parent
abe784b502
commit
475941c295
|
@ -27,7 +27,10 @@
|
|||
/third_party/vulkan-deps
|
||||
/third_party/vulkan_memory_allocator
|
||||
/third_party/zlib
|
||||
/tools
|
||||
/tools/clang
|
||||
/tools/cmake
|
||||
/tools/golang
|
||||
/tools/memory
|
||||
/out
|
||||
|
||||
# Modified from https://www.gitignore.io/api/vim,macos,linux,emacs,windows,sublimetext,visualstudio,visualstudiocode
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2022 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package set implements a basic generic containers
|
||||
package container
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2022 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package container_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func expectEq(t *testing.T, name string, got, expect interface{}) {
|
||||
t.Helper()
|
||||
if diff := cmp.Diff(got, expect); diff != "" {
|
||||
t.Errorf("%v:\n%v", name, diff)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2022 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package container
|
||||
|
||||
// key is the constraint for container keys.
|
||||
// As Map and Set sort before returning a slice, the constraint is equivalent to
|
||||
// the constraints.Ordered in x/exp, instead of 'comparable':
|
||||
// https://cs.opensource.google/go/x/exp/+/master:constraints/constraints.go
|
||||
type key interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 |
|
||||
~uint32 | ~uint64 | ~uintptr | ~float32 | ~float64 | ~string
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Copyright 2022 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package container
|
||||
|
||||
import "sort"
|
||||
|
||||
// Map is a generic unordered map, which wrap's go's builtin 'map'.
|
||||
// K is the map key, which must match the 'key' constraint.
|
||||
// V is the map value, which can be any type.
|
||||
type Map[K key, V any] map[K]V
|
||||
|
||||
// Returns a new empty map
|
||||
func NewMap[K key, V any]() Map[K, V] {
|
||||
return make(Map[K, V])
|
||||
}
|
||||
|
||||
// Add adds an item to the map.
|
||||
func (m Map[K, V]) Add(k K, v V) {
|
||||
m[k] = v
|
||||
}
|
||||
|
||||
// Remove removes an item from the map
|
||||
func (m Map[K, V]) Remove(item K) {
|
||||
delete(m, item)
|
||||
}
|
||||
|
||||
// Contains returns true if the map contains the given item
|
||||
func (m Map[K, V]) Contains(item K) bool {
|
||||
_, found := m[item]
|
||||
return found
|
||||
}
|
||||
|
||||
// Keys returns the sorted keys of the map as a slice
|
||||
func (m Map[K, V]) Keys() []K {
|
||||
out := make([]K, 0, len(m))
|
||||
for v := range m {
|
||||
out = append(out, v)
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i] < out[j] })
|
||||
return out
|
||||
}
|
||||
|
||||
// Values returns the values of the map sorted by key
|
||||
func (m Map[K, V]) Values() []V {
|
||||
out := make([]V, 0, len(m))
|
||||
for _, k := range m.Keys() {
|
||||
out = append(out, m[k])
|
||||
}
|
||||
return out
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2022 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package container_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/container"
|
||||
)
|
||||
|
||||
func TestNewMap(t *testing.T) {
|
||||
m := container.NewMap[string, int]()
|
||||
expectEq(t, "len(m)", len(m), 0)
|
||||
}
|
||||
|
||||
func TestMapAdd(t *testing.T) {
|
||||
m := container.NewMap[string, int]()
|
||||
m.Add("c", 3)
|
||||
expectEq(t, "len(m)", len(m), 1)
|
||||
expectEq(t, `m["a"]`, m["a"], 0)
|
||||
expectEq(t, `m["b"]`, m["b"], 0)
|
||||
expectEq(t, `m["c"]`, m["c"], 3)
|
||||
|
||||
m.Add("a", 1)
|
||||
expectEq(t, "len(m)", len(m), 2)
|
||||
expectEq(t, `m["a"]`, m["a"], 1)
|
||||
expectEq(t, `m["b"]`, m["b"], 0)
|
||||
expectEq(t, `m["c"]`, m["c"], 3)
|
||||
|
||||
m.Add("b", 2)
|
||||
expectEq(t, "len(m)", len(m), 3)
|
||||
expectEq(t, `m["a"]`, m["a"], 1)
|
||||
expectEq(t, `m["b"]`, m["b"], 2)
|
||||
expectEq(t, `m["c"]`, m["c"], 3)
|
||||
}
|
||||
|
||||
func TestMapRemove(t *testing.T) {
|
||||
m := container.NewMap[string, int]()
|
||||
m.Add("a", 1)
|
||||
m.Add("b", 2)
|
||||
m.Add("c", 3)
|
||||
|
||||
m.Remove("c")
|
||||
expectEq(t, "len(m)", len(m), 2)
|
||||
expectEq(t, `m["a"]`, m["a"], 1)
|
||||
expectEq(t, `m["b"]`, m["b"], 2)
|
||||
expectEq(t, `m["c"]`, m["c"], 0)
|
||||
|
||||
m.Remove("a")
|
||||
expectEq(t, "len(m)", len(m), 1)
|
||||
expectEq(t, `m["a"]`, m["a"], 0)
|
||||
expectEq(t, `m["b"]`, m["b"], 2)
|
||||
expectEq(t, `m["c"]`, m["c"], 0)
|
||||
|
||||
m.Remove("b")
|
||||
expectEq(t, "len(m)", len(m), 0)
|
||||
expectEq(t, `m["a"]`, m["a"], 0)
|
||||
expectEq(t, `m["b"]`, m["b"], 0)
|
||||
expectEq(t, `m["c"]`, m["c"], 0)
|
||||
}
|
||||
|
||||
func TestMapContains(t *testing.T) {
|
||||
m := container.NewMap[string, int]()
|
||||
m.Add("c", 3)
|
||||
expectEq(t, `m.Contains("a")`, m.Contains("a"), false)
|
||||
expectEq(t, `m.Contains("b")`, m.Contains("b"), false)
|
||||
expectEq(t, `m.Contains("c")`, m.Contains("c"), true)
|
||||
|
||||
m.Add("a", 1)
|
||||
expectEq(t, `m.Contains("a")`, m.Contains("a"), true)
|
||||
expectEq(t, `m.Contains("b")`, m.Contains("b"), false)
|
||||
expectEq(t, `m.Contains("c")`, m.Contains("c"), true)
|
||||
|
||||
m.Add("b", 2)
|
||||
expectEq(t, `m.Contains("a")`, m.Contains("a"), true)
|
||||
expectEq(t, `m.Contains("b")`, m.Contains("b"), true)
|
||||
expectEq(t, `m.Contains("c")`, m.Contains("c"), true)
|
||||
}
|
||||
|
||||
func TestMapKeys(t *testing.T) {
|
||||
m := container.NewMap[string, int]()
|
||||
m.Add("c", 3)
|
||||
expectEq(t, `m.Keys()`, m.Keys(), []string{"c"})
|
||||
|
||||
m.Add("a", 1)
|
||||
expectEq(t, `m.Keys()`, m.Keys(), []string{"a", "c"})
|
||||
|
||||
m.Add("b", 2)
|
||||
expectEq(t, `m.Keys()`, m.Keys(), []string{"a", "b", "c"})
|
||||
}
|
||||
|
||||
func TestMapValues(t *testing.T) {
|
||||
m := container.NewMap[string, int]()
|
||||
m.Add("c", 1)
|
||||
expectEq(t, `m.Values()`, m.Values(), []int{1})
|
||||
|
||||
m.Add("a", 2)
|
||||
expectEq(t, `m.Values()`, m.Values(), []int{2, 1})
|
||||
|
||||
m.Add("b", 3)
|
||||
expectEq(t, `m.Values()`, m.Values(), []int{2, 3, 1})
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2022 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package container
|
||||
|
||||
import "sort"
|
||||
|
||||
// Set is a generic unordered set, which wrap's go's builtin 'map'.
|
||||
// T is the set key, which must match the 'key' constraint.
|
||||
type Set[T key] map[T]struct{}
|
||||
|
||||
// Returns a new set with the give items
|
||||
func NewSet[T key](items ...T) Set[T] {
|
||||
out := make(Set[T])
|
||||
for _, item := range items {
|
||||
out.Add(item)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Clone returns a new Set populated with s
|
||||
func (s Set[T]) Clone() Set[T] {
|
||||
out := make(Set[T], len(s))
|
||||
for item := range s {
|
||||
out.Add(item)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Add adds an item to the set.
|
||||
func (s Set[T]) Add(item T) {
|
||||
s[item] = struct{}{}
|
||||
}
|
||||
|
||||
// AddAll adds all the items of o to the set.
|
||||
func (s Set[T]) AddAll(o Set[T]) {
|
||||
for item := range o {
|
||||
s.Add(item)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes an item from the set
|
||||
func (s Set[T]) Remove(item T) {
|
||||
delete(s, item)
|
||||
}
|
||||
|
||||
// RemoveAll removes all the items of o from the set.
|
||||
func (s Set[T]) RemoveAll(o Set[T]) {
|
||||
for item := range o {
|
||||
s.Remove(item)
|
||||
}
|
||||
}
|
||||
|
||||
// Contains returns true if the set contains the given item
|
||||
func (s Set[T]) Contains(item T) bool {
|
||||
_, found := s[item]
|
||||
return found
|
||||
}
|
||||
|
||||
// Contains returns true if the set contains all the items in o
|
||||
func (s Set[T]) ContainsAll(o Set[T]) bool {
|
||||
for item := range o {
|
||||
if !s.Contains(item) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Intersection returns true if the set contains all the items in o
|
||||
func (s Set[T]) Intersection(o Set[T]) Set[T] {
|
||||
out := NewSet[T]()
|
||||
for item := range o {
|
||||
if s.Contains(item) {
|
||||
out.Add(item)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// List returns the sorted entries of the set as a slice
|
||||
func (s Set[T]) List() []T {
|
||||
out := make([]T, 0, len(s))
|
||||
for v := range s {
|
||||
out = append(out, v)
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool { return out[i] < out[j] })
|
||||
return out
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
// Copyright 2022 The Dawn Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package container_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"dawn.googlesource.com/dawn/tools/src/container"
|
||||
)
|
||||
|
||||
func TestNewEmptySet(t *testing.T) {
|
||||
s := container.NewSet[string]()
|
||||
expectEq(t, "len(s)", len(s), 0)
|
||||
}
|
||||
|
||||
func TestNewSet(t *testing.T) {
|
||||
s := container.NewSet("c", "a", "b")
|
||||
expectEq(t, "len(s)", len(s), 3)
|
||||
}
|
||||
|
||||
func TestSetList(t *testing.T) {
|
||||
s := container.NewSet("c", "a", "b")
|
||||
expectEq(t, "s.List()", s.List(), []string{"a", "b", "c"})
|
||||
}
|
||||
|
||||
func TestSetClone(t *testing.T) {
|
||||
a := container.NewSet("c", "a", "b")
|
||||
b := a.Clone()
|
||||
a.Remove("a")
|
||||
expectEq(t, "b.List()", b.List(), []string{"a", "b", "c"})
|
||||
}
|
||||
|
||||
func TestSetAdd(t *testing.T) {
|
||||
s := container.NewSet[string]()
|
||||
s.Add("c")
|
||||
expectEq(t, "len(s)", len(s), 1)
|
||||
expectEq(t, "s.List()", s.List(), []string{"c"})
|
||||
|
||||
s.Add("a")
|
||||
expectEq(t, "len(s)", len(s), 2)
|
||||
expectEq(t, "s.List()", s.List(), []string{"a", "c"})
|
||||
|
||||
s.Add("b")
|
||||
expectEq(t, "len(s)", len(s), 3)
|
||||
expectEq(t, "s.List()", s.List(), []string{"a", "b", "c"})
|
||||
}
|
||||
|
||||
func TestSetRemove(t *testing.T) {
|
||||
s := container.NewSet("c", "a", "b")
|
||||
s.Remove("c")
|
||||
expectEq(t, "len(s)", len(s), 2)
|
||||
expectEq(t, "s.List()", s.List(), []string{"a", "b"})
|
||||
|
||||
s.Remove("a")
|
||||
expectEq(t, "len(s)", len(s), 1)
|
||||
expectEq(t, "s.List()", s.List(), []string{"b"})
|
||||
|
||||
s.Remove("b")
|
||||
expectEq(t, "len(s)", len(s), 0)
|
||||
expectEq(t, "s.List()", s.List(), []string{})
|
||||
}
|
||||
|
||||
func TestSetContains(t *testing.T) {
|
||||
s := container.NewSet[string]()
|
||||
s.Add("c")
|
||||
expectEq(t, `m.Contains("a")`, s.Contains("a"), false)
|
||||
expectEq(t, `s.Contains("b")`, s.Contains("b"), false)
|
||||
expectEq(t, `s.Contains("c")`, s.Contains("c"), true)
|
||||
|
||||
s.Add("a")
|
||||
expectEq(t, `s.Contains("a")`, s.Contains("a"), true)
|
||||
expectEq(t, `s.Contains("b")`, s.Contains("b"), false)
|
||||
expectEq(t, `s.Contains("c")`, s.Contains("c"), true)
|
||||
|
||||
s.Add("b")
|
||||
expectEq(t, `s.Contains("a")`, s.Contains("a"), true)
|
||||
expectEq(t, `s.Contains("b")`, s.Contains("b"), true)
|
||||
expectEq(t, `s.Contains("c")`, s.Contains("c"), true)
|
||||
}
|
||||
|
||||
func TestSetContainsAll(t *testing.T) {
|
||||
S := container.NewSet[string]
|
||||
|
||||
s := container.NewSet[string]()
|
||||
s.Add("c")
|
||||
expectEq(t, `s.ContainsAll("a")`, s.ContainsAll(S("a")), false)
|
||||
expectEq(t, `s.ContainsAll("b")`, s.ContainsAll(S("b")), false)
|
||||
expectEq(t, `s.ContainsAll("c")`, s.ContainsAll(S("c")), true)
|
||||
expectEq(t, `s.ContainsAll("a", "b")`, s.ContainsAll(S("a", "b")), false)
|
||||
expectEq(t, `s.ContainsAll("b", "c")`, s.ContainsAll(S("b", "c")), false)
|
||||
expectEq(t, `s.ContainsAll("c", "a")`, s.ContainsAll(S("c", "a")), false)
|
||||
expectEq(t, `s.ContainsAll("c", "a", "b")`, s.ContainsAll(S("c", "a", "b")), false)
|
||||
|
||||
s.Add("a")
|
||||
expectEq(t, `s.ContainsAll("a")`, s.ContainsAll(S("a")), true)
|
||||
expectEq(t, `s.ContainsAll("b")`, s.ContainsAll(S("b")), false)
|
||||
expectEq(t, `s.ContainsAll("c")`, s.ContainsAll(S("c")), true)
|
||||
expectEq(t, `s.ContainsAll("a", "b")`, s.ContainsAll(S("a", "b")), false)
|
||||
expectEq(t, `s.ContainsAll("b", "c")`, s.ContainsAll(S("b", "c")), false)
|
||||
expectEq(t, `s.ContainsAll("c", "a")`, s.ContainsAll(S("c", "a")), true)
|
||||
expectEq(t, `s.ContainsAll("c", "a", "b")`, s.ContainsAll(S("c", "a", "b")), false)
|
||||
|
||||
s.Add("b")
|
||||
expectEq(t, `s.ContainsAll("a")`, s.ContainsAll(S("a")), true)
|
||||
expectEq(t, `s.ContainsAll("b")`, s.ContainsAll(S("b")), true)
|
||||
expectEq(t, `s.ContainsAll("c")`, s.ContainsAll(S("c")), true)
|
||||
expectEq(t, `s.ContainsAll("a", "b")`, s.ContainsAll(S("a", "b")), true)
|
||||
expectEq(t, `s.ContainsAll("b", "c")`, s.ContainsAll(S("b", "c")), true)
|
||||
expectEq(t, `s.ContainsAll("c", "a")`, s.ContainsAll(S("c", "a")), true)
|
||||
expectEq(t, `s.ContainsAll("c", "a", "b")`, s.ContainsAll(S("c", "a", "b")), true)
|
||||
}
|
||||
|
||||
func TestSetIntersection(t *testing.T) {
|
||||
a := container.NewSet(1, 3, 4, 6)
|
||||
b := container.NewSet(2, 3, 4, 5)
|
||||
|
||||
i := a.Intersection(b)
|
||||
expectEq(t, `i.List()`, i.List(), []int{3, 4})
|
||||
}
|
||||
|
||||
func TestSetAddAll(t *testing.T) {
|
||||
s := container.NewSet[string]()
|
||||
s.AddAll(container.NewSet("c", "a"))
|
||||
expectEq(t, "len(s)", len(s), 2)
|
||||
expectEq(t, "s.List()", s.List(), []string{"a", "c"})
|
||||
}
|
||||
|
||||
func TestSetRemoveAll(t *testing.T) {
|
||||
s := container.NewSet("c", "a", "b")
|
||||
s.RemoveAll(container.NewSet("c", "a"))
|
||||
expectEq(t, "len(s)", len(s), 1)
|
||||
expectEq(t, "s.List()", s.List(), []string{"b"})
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
module dawn.googlesource.com/dawn/tools/src
|
||||
|
||||
go 1.18
|
||||
|
||||
require github.com/google/go-cmp v0.5.6
|
||||
|
||||
require golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
|
@ -0,0 +1,5 @@
|
|||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
Loading…
Reference in New Issue