Loading android/paths.go +22 −0 Original line number Diff line number Diff line Loading @@ -470,6 +470,14 @@ func (p Paths) Strings() []string { // FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It // modifies the Paths slice contents in place, and returns a subslice of the original slice. func FirstUniquePaths(list Paths) Paths { // 128 was chosen based on BenchmarkFirstUniquePaths results. if len(list) > 128 { return firstUniquePathsMap(list) } return firstUniquePathsList(list) } func firstUniquePathsList(list Paths) Paths { k := 0 outer: for i := 0; i < len(list); i++ { Loading @@ -484,6 +492,20 @@ outer: return list[:k] } func firstUniquePathsMap(list Paths) Paths { k := 0 seen := make(map[Path]bool, len(list)) for i := 0; i < len(list); i++ { if seen[list[i]] { continue } seen[list[i]] = true list[k] = list[i] k++ } return list[:k] } // LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It // modifies the Paths slice contents in place, and returns a subslice of the original slice. func LastUniquePaths(list Paths) Paths { Loading android/paths_test.go +49 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ import ( "errors" "fmt" "reflect" "strconv" "strings" "testing" Loading Loading @@ -1255,3 +1256,51 @@ func ExampleOutputPath_FileInSameDir() { // out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex // boot.art oat/arm/boot.vdex } func BenchmarkFirstUniquePaths(b *testing.B) { implementations := []struct { name string f func(Paths) Paths }{ { name: "list", f: firstUniquePathsList, }, { name: "map", f: firstUniquePathsMap, }, } const maxSize = 1024 uniquePaths := make(Paths, maxSize) for i := range uniquePaths { uniquePaths[i] = PathForTesting(strconv.Itoa(i)) } samePath := make(Paths, maxSize) for i := range samePath { samePath[i] = uniquePaths[0] } f := func(b *testing.B, imp func(Paths) Paths, paths Paths) { for i := 0; i < b.N; i++ { b.ReportAllocs() paths = append(Paths(nil), paths...) imp(paths) } } for n := 1; n <= maxSize; n <<= 1 { b.Run(strconv.Itoa(n), func(b *testing.B) { for _, implementation := range implementations { b.Run(implementation.name, func(b *testing.B) { b.Run("same", func(b *testing.B) { f(b, implementation.f, samePath[:n]) }) b.Run("unique", func(b *testing.B) { f(b, implementation.f, uniquePaths[:n]) }) }) } }) } } android/util.go +22 −0 Original line number Diff line number Diff line Loading @@ -193,6 +193,14 @@ func RemoveFromList(s string, list []string) (bool, []string) { // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of // each. It modifies the slice contents in place, and returns a subslice of the original slice. func FirstUniqueStrings(list []string) []string { // 128 was chosen based on BenchmarkFirstUniqueStrings results. if len(list) > 128 { return firstUniqueStringsMap(list) } return firstUniqueStringsList(list) } func firstUniqueStringsList(list []string) []string { k := 0 outer: for i := 0; i < len(list); i++ { Loading @@ -207,6 +215,20 @@ outer: return list[:k] } func firstUniqueStringsMap(list []string) []string { k := 0 seen := make(map[string]bool, len(list)) for i := 0; i < len(list); i++ { if seen[list[i]] { continue } seen[list[i]] = true list[k] = list[i] k++ } return list[:k] } // LastUniqueStrings returns all unique elements of a slice of strings, keeping the last copy of // each. It modifies the slice contents in place, and returns a subslice of the original slice. func LastUniqueStrings(list []string) []string { Loading android/util_test.go +64 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android import ( "fmt" "reflect" "strconv" "testing" ) Loading Loading @@ -59,15 +60,25 @@ var firstUniqueStringsTestCases = []struct { } func TestFirstUniqueStrings(t *testing.T) { for _, testCase := range firstUniqueStringsTestCases { out := FirstUniqueStrings(testCase.in) if !reflect.DeepEqual(out, testCase.out) { f := func(t *testing.T, imp func([]string) []string, in, want []string) { t.Helper() out := imp(in) if !reflect.DeepEqual(out, want) { t.Errorf("incorrect output:") t.Errorf(" input: %#v", testCase.in) t.Errorf(" expected: %#v", testCase.out) t.Errorf(" input: %#v", in) t.Errorf(" expected: %#v", want) t.Errorf(" got: %#v", out) } } for _, testCase := range firstUniqueStringsTestCases { t.Run("list", func(t *testing.T) { f(t, firstUniqueStringsList, testCase.in, testCase.out) }) t.Run("map", func(t *testing.T) { f(t, firstUniqueStringsMap, testCase.in, testCase.out) }) } } var lastUniqueStringsTestCases = []struct { Loading Loading @@ -568,3 +579,51 @@ func Test_Shard(t *testing.T) { }) } } func BenchmarkFirstUniqueStrings(b *testing.B) { implementations := []struct { name string f func([]string) []string }{ { name: "list", f: firstUniqueStringsList, }, { name: "map", f: firstUniqueStringsMap, }, } const maxSize = 1024 uniqueStrings := make([]string, maxSize) for i := range uniqueStrings { uniqueStrings[i] = strconv.Itoa(i) } sameString := make([]string, maxSize) for i := range sameString { sameString[i] = uniqueStrings[0] } f := func(b *testing.B, imp func([]string) []string, s []string) { for i := 0; i < b.N; i++ { b.ReportAllocs() s = append([]string(nil), s...) imp(s) } } for n := 1; n <= maxSize; n <<= 1 { b.Run(strconv.Itoa(n), func(b *testing.B) { for _, implementation := range implementations { b.Run(implementation.name, func(b *testing.B) { b.Run("same", func(b *testing.B) { f(b, implementation.f, sameString[:n]) }) b.Run("unique", func(b *testing.B) { f(b, implementation.f, uniqueStrings[:n]) }) }) } }) } } Loading
android/paths.go +22 −0 Original line number Diff line number Diff line Loading @@ -470,6 +470,14 @@ func (p Paths) Strings() []string { // FirstUniquePaths returns all unique elements of a Paths, keeping the first copy of each. It // modifies the Paths slice contents in place, and returns a subslice of the original slice. func FirstUniquePaths(list Paths) Paths { // 128 was chosen based on BenchmarkFirstUniquePaths results. if len(list) > 128 { return firstUniquePathsMap(list) } return firstUniquePathsList(list) } func firstUniquePathsList(list Paths) Paths { k := 0 outer: for i := 0; i < len(list); i++ { Loading @@ -484,6 +492,20 @@ outer: return list[:k] } func firstUniquePathsMap(list Paths) Paths { k := 0 seen := make(map[Path]bool, len(list)) for i := 0; i < len(list); i++ { if seen[list[i]] { continue } seen[list[i]] = true list[k] = list[i] k++ } return list[:k] } // LastUniquePaths returns all unique elements of a Paths, keeping the last copy of each. It // modifies the Paths slice contents in place, and returns a subslice of the original slice. func LastUniquePaths(list Paths) Paths { Loading
android/paths_test.go +49 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ import ( "errors" "fmt" "reflect" "strconv" "strings" "testing" Loading Loading @@ -1255,3 +1256,51 @@ func ExampleOutputPath_FileInSameDir() { // out/system/framework/boot.art out/system/framework/oat/arm/boot.vdex // boot.art oat/arm/boot.vdex } func BenchmarkFirstUniquePaths(b *testing.B) { implementations := []struct { name string f func(Paths) Paths }{ { name: "list", f: firstUniquePathsList, }, { name: "map", f: firstUniquePathsMap, }, } const maxSize = 1024 uniquePaths := make(Paths, maxSize) for i := range uniquePaths { uniquePaths[i] = PathForTesting(strconv.Itoa(i)) } samePath := make(Paths, maxSize) for i := range samePath { samePath[i] = uniquePaths[0] } f := func(b *testing.B, imp func(Paths) Paths, paths Paths) { for i := 0; i < b.N; i++ { b.ReportAllocs() paths = append(Paths(nil), paths...) imp(paths) } } for n := 1; n <= maxSize; n <<= 1 { b.Run(strconv.Itoa(n), func(b *testing.B) { for _, implementation := range implementations { b.Run(implementation.name, func(b *testing.B) { b.Run("same", func(b *testing.B) { f(b, implementation.f, samePath[:n]) }) b.Run("unique", func(b *testing.B) { f(b, implementation.f, uniquePaths[:n]) }) }) } }) } }
android/util.go +22 −0 Original line number Diff line number Diff line Loading @@ -193,6 +193,14 @@ func RemoveFromList(s string, list []string) (bool, []string) { // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of // each. It modifies the slice contents in place, and returns a subslice of the original slice. func FirstUniqueStrings(list []string) []string { // 128 was chosen based on BenchmarkFirstUniqueStrings results. if len(list) > 128 { return firstUniqueStringsMap(list) } return firstUniqueStringsList(list) } func firstUniqueStringsList(list []string) []string { k := 0 outer: for i := 0; i < len(list); i++ { Loading @@ -207,6 +215,20 @@ outer: return list[:k] } func firstUniqueStringsMap(list []string) []string { k := 0 seen := make(map[string]bool, len(list)) for i := 0; i < len(list); i++ { if seen[list[i]] { continue } seen[list[i]] = true list[k] = list[i] k++ } return list[:k] } // LastUniqueStrings returns all unique elements of a slice of strings, keeping the last copy of // each. It modifies the slice contents in place, and returns a subslice of the original slice. func LastUniqueStrings(list []string) []string { Loading
android/util_test.go +64 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android import ( "fmt" "reflect" "strconv" "testing" ) Loading Loading @@ -59,15 +60,25 @@ var firstUniqueStringsTestCases = []struct { } func TestFirstUniqueStrings(t *testing.T) { for _, testCase := range firstUniqueStringsTestCases { out := FirstUniqueStrings(testCase.in) if !reflect.DeepEqual(out, testCase.out) { f := func(t *testing.T, imp func([]string) []string, in, want []string) { t.Helper() out := imp(in) if !reflect.DeepEqual(out, want) { t.Errorf("incorrect output:") t.Errorf(" input: %#v", testCase.in) t.Errorf(" expected: %#v", testCase.out) t.Errorf(" input: %#v", in) t.Errorf(" expected: %#v", want) t.Errorf(" got: %#v", out) } } for _, testCase := range firstUniqueStringsTestCases { t.Run("list", func(t *testing.T) { f(t, firstUniqueStringsList, testCase.in, testCase.out) }) t.Run("map", func(t *testing.T) { f(t, firstUniqueStringsMap, testCase.in, testCase.out) }) } } var lastUniqueStringsTestCases = []struct { Loading Loading @@ -568,3 +579,51 @@ func Test_Shard(t *testing.T) { }) } } func BenchmarkFirstUniqueStrings(b *testing.B) { implementations := []struct { name string f func([]string) []string }{ { name: "list", f: firstUniqueStringsList, }, { name: "map", f: firstUniqueStringsMap, }, } const maxSize = 1024 uniqueStrings := make([]string, maxSize) for i := range uniqueStrings { uniqueStrings[i] = strconv.Itoa(i) } sameString := make([]string, maxSize) for i := range sameString { sameString[i] = uniqueStrings[0] } f := func(b *testing.B, imp func([]string) []string, s []string) { for i := 0; i < b.N; i++ { b.ReportAllocs() s = append([]string(nil), s...) imp(s) } } for n := 1; n <= maxSize; n <<= 1 { b.Run(strconv.Itoa(n), func(b *testing.B) { for _, implementation := range implementations { b.Run(implementation.name, func(b *testing.B) { b.Run("same", func(b *testing.B) { f(b, implementation.f, sameString[:n]) }) b.Run("unique", func(b *testing.B) { f(b, implementation.f, uniqueStrings[:n]) }) }) } }) } }