• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
18	"cmp"
19	"fmt"
20	"path/filepath"
21	"reflect"
22	"regexp"
23	"runtime"
24	"sort"
25	"strings"
26
27	"github.com/google/blueprint/proptools"
28)
29
30// CopyOf returns a new slice that has the same contents as s.
31func CopyOf[T any](s []T) []T {
32	// If the input is nil, return nil and not an empty list
33	if s == nil {
34		return s
35	}
36	return append([]T{}, s...)
37}
38
39// Concat returns a new slice concatenated from the two input slices. It does not change the input
40// slices.
41func Concat[T any](s1, s2 []T) []T {
42	res := make([]T, 0, len(s1)+len(s2))
43	res = append(res, s1...)
44	res = append(res, s2...)
45	return res
46}
47
48// JoinPathsWithPrefix converts the paths to strings, prefixes them
49// with prefix and then joins them separated by " ".
50func JoinPathsWithPrefix(paths []Path, prefix string) string {
51	strs := make([]string, len(paths))
52	for i := range paths {
53		strs[i] = paths[i].String()
54	}
55	return JoinWithPrefixAndSeparator(strs, prefix, " ")
56}
57
58// JoinWithPrefix prepends the prefix to each string in the list and
59// returns them joined together with " " as separator.
60func JoinWithPrefix(strs []string, prefix string) string {
61	return JoinWithPrefixAndSeparator(strs, prefix, " ")
62}
63
64// JoinWithPrefixAndSeparator prepends the prefix to each string in the list and
65// returns them joined together with the given separator.
66func JoinWithPrefixAndSeparator(strs []string, prefix string, sep string) string {
67	return JoinWithPrefixSuffixAndSeparator(strs, prefix, "", sep)
68}
69
70// JoinWithSuffixAndSeparator appends the suffix to each string in the list and
71// returns them joined together with the given separator.
72func JoinWithSuffixAndSeparator(strs []string, suffix string, sep string) string {
73	return JoinWithPrefixSuffixAndSeparator(strs, "", suffix, sep)
74}
75
76// JoinWithPrefixSuffixAndSeparator appends the prefix/suffix to each string in the list and
77// returns them joined together with the given separator.
78func JoinWithPrefixSuffixAndSeparator(strs []string, prefix, suffix, sep string) string {
79	if len(strs) == 0 {
80		return ""
81	}
82
83	// Pre-calculate the length of the result
84	length := 0
85	for _, s := range strs {
86		length += len(s)
87	}
88	length += (len(prefix)+len(suffix))*len(strs) + len(sep)*(len(strs)-1)
89
90	var buf strings.Builder
91	buf.Grow(length)
92	buf.WriteString(prefix)
93	buf.WriteString(strs[0])
94	buf.WriteString(suffix)
95	for i := 1; i < len(strs); i++ {
96		buf.WriteString(sep)
97		buf.WriteString(prefix)
98		buf.WriteString(strs[i])
99		buf.WriteString(suffix)
100	}
101	return buf.String()
102}
103
104// SortedKeys returns the keys of the given map in the ascending order.
105func SortedKeys[T cmp.Ordered, V any](m map[T]V) []T {
106	if len(m) == 0 {
107		return nil
108	}
109	ret := make([]T, 0, len(m))
110	for k := range m {
111		ret = append(ret, k)
112	}
113	sort.Slice(ret, func(i, j int) bool {
114		return ret[i] < ret[j]
115	})
116	return ret
117}
118
119// stringValues returns the values of the given string-valued map in randomized map order.
120func stringValues(m interface{}) []string {
121	v := reflect.ValueOf(m)
122	if v.Kind() != reflect.Map {
123		panic(fmt.Sprintf("%#v is not a map", m))
124	}
125	if v.Len() == 0 {
126		return nil
127	}
128	iter := v.MapRange()
129	s := make([]string, 0, v.Len())
130	for iter.Next() {
131		s = append(s, iter.Value().String())
132	}
133	return s
134}
135
136// SortedStringValues returns the values of the given string-valued map in the ascending order.
137func SortedStringValues(m interface{}) []string {
138	s := stringValues(m)
139	sort.Strings(s)
140	return s
141}
142
143// SortedUniqueStringValues returns the values of the given string-valued map in the ascending order
144// with duplicates removed.
145func SortedUniqueStringValues(m interface{}) []string {
146	s := stringValues(m)
147	return SortedUniqueStrings(s)
148}
149
150// IndexList returns the index of the first occurrence of the given string in the list or -1
151func IndexList[T comparable](t T, list []T) int {
152	for i, l := range list {
153		if l == t {
154			return i
155		}
156	}
157	return -1
158}
159
160func InList[T comparable](t T, list []T) bool {
161	return IndexList(t, list) != -1
162}
163
164func setFromList[T comparable](l []T) map[T]bool {
165	m := make(map[T]bool, len(l))
166	for _, t := range l {
167		m[t] = true
168	}
169	return m
170}
171
172// PrettyConcat returns the formatted concatenated string suitable for displaying user-facing
173// messages.
174func PrettyConcat(list []string, quote bool, lastSep string) string {
175	if len(list) == 0 {
176		return ""
177	}
178
179	quoteStr := func(v string) string {
180		if !quote {
181			return v
182		}
183		return fmt.Sprintf("%q", v)
184	}
185
186	if len(list) == 1 {
187		return quoteStr(list[0])
188	}
189
190	var sb strings.Builder
191	for i, val := range list {
192		if i > 0 {
193			sb.WriteString(", ")
194		}
195		if i == len(list)-1 {
196			sb.WriteString(lastSep)
197			if lastSep != "" {
198				sb.WriteString(" ")
199			}
200		}
201		sb.WriteString(quoteStr(val))
202	}
203
204	return sb.String()
205}
206
207// ListSetDifference checks if the two lists contain the same elements. It returns
208// a boolean which is true if there is a difference, and then returns lists of unique elements
209// that are in l1 but not l2, and l2 but not l1.
210func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) {
211	listsDiffer := false
212	l1 = firstUnique(l1)
213	l2 = firstUnique(l2)
214	diff1 := []T{}
215	diff2 := []T{}
216	m1 := setFromList(l1)
217	m2 := setFromList(l2)
218	for _, t := range l1 {
219		if _, ok := m2[t]; !ok {
220			diff1 = append(diff1, t)
221			listsDiffer = true
222		}
223	}
224	for _, t := range l2 {
225		if _, ok := m1[t]; !ok {
226			diff2 = append(diff2, t)
227			listsDiffer = true
228		}
229	}
230	return listsDiffer, diff1, diff2
231}
232
233// Returns true if the two lists have common elements.
234func HasIntersection[T comparable](l1, l2 []T) bool {
235	m1 := setFromList(l1)
236	for _, x := range l2 {
237		if _, ok := m1[x]; ok {
238			return true
239		}
240	}
241	return false
242}
243
244// Returns true if the given string s is prefixed with any string in the given prefix list.
245func HasAnyPrefix(s string, prefixList []string) bool {
246	for _, prefix := range prefixList {
247		if strings.HasPrefix(s, prefix) {
248			return true
249		}
250	}
251	return false
252}
253
254// Returns true if any string in the given list has the given substring.
255func SubstringInList(list []string, substr string) bool {
256	for _, s := range list {
257		if strings.Contains(s, substr) {
258			return true
259		}
260	}
261	return false
262}
263
264// Returns true if any string in the given list has the given prefix.
265func PrefixInList(list []string, prefix string) bool {
266	for _, s := range list {
267		if strings.HasPrefix(s, prefix) {
268			return true
269		}
270	}
271	return false
272}
273
274// Returns true if any string in the given list has the given suffix.
275func SuffixInList(list []string, suffix string) bool {
276	for _, s := range list {
277		if strings.HasSuffix(s, suffix) {
278			return true
279		}
280	}
281	return false
282}
283
284// IndexListPred returns the index of the element which in the given `list` satisfying the predicate, or -1 if there is no such element.
285func IndexListPred(pred func(s string) bool, list []string) int {
286	for i, l := range list {
287		if pred(l) {
288			return i
289		}
290	}
291
292	return -1
293}
294
295// FilterList divides the string list into two lists: one with the strings belonging
296// to the given filter list, and the other with the remaining ones
297func FilterList(list []string, filter []string) (remainder []string, filtered []string) {
298	// InList is O(n). May be worth using more efficient lookup for longer lists.
299	for _, l := range list {
300		if InList(l, filter) {
301			filtered = append(filtered, l)
302		} else {
303			remainder = append(remainder, l)
304		}
305	}
306
307	return
308}
309
310// FilterListByPrefixes performs the same splitting as FilterList does, but treats the passed
311// filters as prefixes
312func FilterListByPrefix(list []string, filter []string) (remainder []string, filtered []string) {
313	for _, l := range list {
314		if HasAnyPrefix(l, filter) {
315			filtered = append(filtered, l)
316		} else {
317			remainder = append(remainder, l)
318		}
319	}
320
321	return
322}
323
324// FilterListPred returns the elements of the given list for which the predicate
325// returns true. Order is kept.
326func FilterListPred(list []string, pred func(s string) bool) (filtered []string) {
327	for _, l := range list {
328		if pred(l) {
329			filtered = append(filtered, l)
330		}
331	}
332	return
333}
334
335// RemoveListFromList removes the strings belonging to the filter list from the
336// given list and returns the result
337func RemoveListFromList(list []string, filter_out []string) (result []string) {
338	result = make([]string, 0, len(list))
339	for _, l := range list {
340		if !InList(l, filter_out) {
341			result = append(result, l)
342		}
343	}
344	return
345}
346
347// RemoveFromList removes given string from the string list.
348func RemoveFromList(s string, list []string) (bool, []string) {
349	result := make([]string, 0, len(list))
350	var removed bool
351	for _, item := range list {
352		if item != s {
353			result = append(result, item)
354		} else {
355			removed = true
356		}
357	}
358	return removed, result
359}
360
361// FirstUniqueFunc returns all unique elements of a slice, keeping the first copy of
362// each.  It does not modify the input slice. The eq function should return true
363// if two elements can be considered equal.
364func FirstUniqueFunc[SortableList ~[]Sortable, Sortable any](list SortableList, eq func(a, b Sortable) bool) SortableList {
365	k := 0
366outer:
367	for i := 0; i < len(list); i++ {
368		for j := 0; j < k; j++ {
369			if eq(list[i], list[j]) {
370				continue outer
371			}
372		}
373		list[k] = list[i]
374		k++
375	}
376	return list[:k]
377}
378
379// FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
380// each.  It does not modify the input slice.
381func FirstUniqueStrings(list []string) []string {
382	return firstUnique(list)
383}
384
385// firstUnique returns all unique elements of a slice, keeping the first copy of each.  It
386// does not modify the input slice.
387func firstUnique[T comparable](slice []T) []T {
388	// Do not modify the input in-place, operate on a copy instead.
389	slice = CopyOf(slice)
390	return firstUniqueInPlace(slice)
391}
392
393// firstUniqueInPlace returns all unique elements of a slice, keeping the first copy of
394// each.  It modifies the slice contents in place, and returns a subslice of the original
395// slice.
396func firstUniqueInPlace[T comparable](slice []T) []T {
397	// 128 was chosen based on BenchmarkFirstUniqueStrings results.
398	if len(slice) > 128 {
399		return firstUniqueMap(slice)
400	}
401	return firstUniqueList(slice)
402}
403
404// firstUniqueList is an implementation of firstUnique using an O(N^2) list comparison to look for
405// duplicates.
406func firstUniqueList[T any](in []T) []T {
407	writeIndex := 0
408outer:
409	for readIndex := 0; readIndex < len(in); readIndex++ {
410		for compareIndex := 0; compareIndex < writeIndex; compareIndex++ {
411			if interface{}(in[readIndex]) == interface{}(in[compareIndex]) {
412				// The value at readIndex already exists somewhere in the output region
413				// of the slice before writeIndex, skip it.
414				continue outer
415			}
416		}
417		if readIndex != writeIndex {
418			in[writeIndex] = in[readIndex]
419		}
420		writeIndex++
421	}
422	return in[0:writeIndex]
423}
424
425// firstUniqueMap is an implementation of firstUnique using an O(N) hash set lookup to look for
426// duplicates.
427func firstUniqueMap[T comparable](in []T) []T {
428	writeIndex := 0
429	seen := make(map[T]bool, len(in))
430	for readIndex := 0; readIndex < len(in); readIndex++ {
431		if _, exists := seen[in[readIndex]]; exists {
432			continue
433		}
434		seen[in[readIndex]] = true
435		if readIndex != writeIndex {
436			in[writeIndex] = in[readIndex]
437		}
438		writeIndex++
439	}
440	return in[0:writeIndex]
441}
442
443// ReverseSliceInPlace reverses the elements of a slice in place and returns it.
444func ReverseSliceInPlace[T any](in []T) []T {
445	for i, j := 0, len(in)-1; i < j; i, j = i+1, j-1 {
446		in[i], in[j] = in[j], in[i]
447	}
448	return in
449}
450
451// ReverseSlice returns a copy of a slice in reverse order.
452func ReverseSlice[T any](in []T) []T {
453	if in == nil {
454		return in
455	}
456	out := make([]T, len(in))
457	for i := 0; i < len(in); i++ {
458		out[i] = in[len(in)-1-i]
459	}
460	return out
461}
462
463// LastUniqueStrings returns all unique elements of a slice of strings, keeping the last copy of
464// each.  It modifies the slice contents in place, and returns a subslice of the original slice.
465func LastUniqueStrings(list []string) []string {
466	totalSkip := 0
467	for i := len(list) - 1; i >= totalSkip; i-- {
468		skip := 0
469		for j := i - 1; j >= totalSkip; j-- {
470			if list[i] == list[j] {
471				skip++
472			} else {
473				list[j+skip] = list[j]
474			}
475		}
476		totalSkip += skip
477	}
478	return list[totalSkip:]
479}
480
481// SortedUniqueStrings returns what the name says
482func SortedUniqueStrings(list []string) []string {
483	// FirstUniqueStrings creates a copy of `list`, so the input remains untouched.
484	unique := FirstUniqueStrings(list)
485	sort.Strings(unique)
486	return unique
487}
488
489// checkCalledFromInit panics if a Go package's init function is not on the
490// call stack.
491func checkCalledFromInit() {
492	for skip := 3; ; skip++ {
493		_, funcName, ok := callerName(skip)
494		if !ok {
495			panic("not called from an init func")
496		}
497
498		if funcName == "init" || strings.HasPrefix(funcName, "init·") ||
499			strings.HasPrefix(funcName, "init.") {
500			return
501		}
502	}
503}
504
505// A regex to find a package path within a function name. It finds the shortest string that is
506// followed by '.' and doesn't have any '/'s left.
507var pkgPathRe = regexp.MustCompile(`^(.*?)\.([^/]+)$`)
508
509// callerName returns the package path and function name of the calling
510// function.  The skip argument has the same meaning as the skip argument of
511// runtime.Callers.
512func callerName(skip int) (pkgPath, funcName string, ok bool) {
513	var pc [1]uintptr
514	n := runtime.Callers(skip+1, pc[:])
515	if n != 1 {
516		return "", "", false
517	}
518
519	f := runtime.FuncForPC(pc[0]).Name()
520	s := pkgPathRe.FindStringSubmatch(f)
521	if len(s) < 3 {
522		panic(fmt.Errorf("failed to extract package path and function name from %q", f))
523	}
524
525	return s[1], s[2], true
526}
527
528// GetNumericSdkVersion removes the first occurrence of system_ in a string,
529// which is assumed to be something like "system_1.2.3"
530func GetNumericSdkVersion(v string) string {
531	return strings.Replace(v, "system_", "", 1)
532}
533
534// copied from build/kati/strutil.go
535func substPattern(pat, repl, str string) string {
536	ps := strings.SplitN(pat, "%", 2)
537	if len(ps) != 2 {
538		if str == pat {
539			return repl
540		}
541		return str
542	}
543	in := str
544	trimmed := str
545	if ps[0] != "" {
546		trimmed = strings.TrimPrefix(in, ps[0])
547		if trimmed == in {
548			return str
549		}
550	}
551	in = trimmed
552	if ps[1] != "" {
553		trimmed = strings.TrimSuffix(in, ps[1])
554		if trimmed == in {
555			return str
556		}
557	}
558
559	rs := strings.SplitN(repl, "%", 2)
560	if len(rs) != 2 {
561		return repl
562	}
563	return rs[0] + trimmed + rs[1]
564}
565
566// copied from build/kati/strutil.go
567func matchPattern(pat, str string) bool {
568	i := strings.IndexByte(pat, '%')
569	if i < 0 {
570		return pat == str
571	}
572	return strings.HasPrefix(str, pat[:i]) && strings.HasSuffix(str, pat[i+1:])
573}
574
575var shlibVersionPattern = regexp.MustCompile("(?:\\.\\d+(?:svn)?)+")
576
577// splitFileExt splits a file name into root, suffix and ext. root stands for the file name without
578// the file extension and the version number (e.g. "libexample"). suffix stands for the
579// concatenation of the file extension and the version number (e.g. ".so.1.0"). ext stands for the
580// file extension after the version numbers are trimmed (e.g. ".so").
581func SplitFileExt(name string) (string, string, string) {
582	// Extract and trim the shared lib version number if the file name ends with dot digits.
583	suffix := ""
584	matches := shlibVersionPattern.FindAllStringIndex(name, -1)
585	if len(matches) > 0 {
586		lastMatch := matches[len(matches)-1]
587		if lastMatch[1] == len(name) {
588			suffix = name[lastMatch[0]:lastMatch[1]]
589			name = name[0:lastMatch[0]]
590		}
591	}
592
593	// Extract the file name root and the file extension.
594	ext := filepath.Ext(name)
595	root := strings.TrimSuffix(name, ext)
596	suffix = ext + suffix
597
598	return root, suffix, ext
599}
600
601// ShardPaths takes a Paths, and returns a slice of Paths where each one has at most shardSize paths.
602func ShardPaths(paths Paths, shardSize int) []Paths {
603	return proptools.ShardBySize(paths, shardSize)
604}
605
606// ShardString takes a string and returns a slice of strings where the length of each one is
607// at most shardSize.
608func ShardString(s string, shardSize int) []string {
609	if len(s) == 0 {
610		return nil
611	}
612	ret := make([]string, 0, (len(s)+shardSize-1)/shardSize)
613	for len(s) > shardSize {
614		ret = append(ret, s[0:shardSize])
615		s = s[shardSize:]
616	}
617	if len(s) > 0 {
618		ret = append(ret, s)
619	}
620	return ret
621}
622
623// ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize
624// elements.
625func ShardStrings(s []string, shardSize int) [][]string {
626	return proptools.ShardBySize(s, shardSize)
627}
628
629// CheckDuplicate checks if there are duplicates in given string list.
630// If there are, it returns first such duplicate and true.
631func CheckDuplicate(values []string) (duplicate string, found bool) {
632	seen := make(map[string]string)
633	for _, v := range values {
634		if duplicate, found = seen[v]; found {
635			return duplicate, true
636		}
637		seen[v] = v
638	}
639	return "", false
640}
641
642func AddToStringSet(set map[string]bool, items []string) {
643	for _, item := range items {
644		set[item] = true
645	}
646}
647
648// AppendIfNotZero append the given value to the slice if it is not the zero value
649// for its type.
650func AppendIfNotZero[T comparable](slice []T, value T) []T {
651	var zeroValue T // Get the zero value of the type T
652	if value != zeroValue {
653		return append(slice, value)
654	}
655	return slice
656}
657