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