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 "fmt" 19 "path/filepath" 20 "reflect" 21 "regexp" 22 "runtime" 23 "sort" 24 "strings" 25) 26 27// CopyOf returns a new slice that has the same contents as s. 28func CopyOf(s []string) []string { 29 // If the input is nil, return nil and not an empty list 30 if s == nil { 31 return s 32 } 33 return append([]string{}, s...) 34} 35 36// Concat returns a new slice concatenated from the two input slices. It does not change the input 37// slices. 38func Concat[T any](s1, s2 []T) []T { 39 res := make([]T, 0, len(s1)+len(s2)) 40 res = append(res, s1...) 41 res = append(res, s2...) 42 return res 43} 44 45// JoinWithPrefix prepends the prefix to each string in the list and 46// returns them joined together with " " as separator. 47func JoinWithPrefix(strs []string, prefix string) string { 48 return JoinWithPrefixAndSeparator(strs, prefix, " ") 49} 50 51// JoinWithPrefixAndSeparator prepends the prefix to each string in the list and 52// returns them joined together with the given separator. 53func JoinWithPrefixAndSeparator(strs []string, prefix string, sep string) string { 54 if len(strs) == 0 { 55 return "" 56 } 57 58 var buf strings.Builder 59 buf.WriteString(prefix) 60 buf.WriteString(strs[0]) 61 for i := 1; i < len(strs); i++ { 62 buf.WriteString(sep) 63 buf.WriteString(prefix) 64 buf.WriteString(strs[i]) 65 } 66 return buf.String() 67} 68 69// SortedStringKeys returns the keys of the given map in the ascending order. 70// 71// Deprecated: Use SortedKeys instead. 72func SortedStringKeys[V any](m map[string]V) []string { 73 return SortedKeys(m) 74} 75 76type Ordered interface { 77 ~string | 78 ~float32 | ~float64 | 79 ~int | ~int8 | ~int16 | ~int32 | ~int64 | 80 ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr 81} 82 83// SortedKeys returns the keys of the given map in the ascending order. 84func SortedKeys[T Ordered, V any](m map[T]V) []T { 85 if len(m) == 0 { 86 return nil 87 } 88 ret := make([]T, 0, len(m)) 89 for k := range m { 90 ret = append(ret, k) 91 } 92 sort.Slice(ret, func(i, j int) bool { 93 return ret[i] < ret[j] 94 }) 95 return ret 96} 97 98// stringValues returns the values of the given string-valued map in randomized map order. 99func stringValues(m interface{}) []string { 100 v := reflect.ValueOf(m) 101 if v.Kind() != reflect.Map { 102 panic(fmt.Sprintf("%#v is not a map", m)) 103 } 104 if v.Len() == 0 { 105 return nil 106 } 107 iter := v.MapRange() 108 s := make([]string, 0, v.Len()) 109 for iter.Next() { 110 s = append(s, iter.Value().String()) 111 } 112 return s 113} 114 115// SortedStringValues returns the values of the given string-valued map in the ascending order. 116func SortedStringValues(m interface{}) []string { 117 s := stringValues(m) 118 sort.Strings(s) 119 return s 120} 121 122// SortedUniqueStringValues returns the values of the given string-valued map in the ascending order 123// with duplicates removed. 124func SortedUniqueStringValues(m interface{}) []string { 125 s := stringValues(m) 126 return SortedUniqueStrings(s) 127} 128 129// IndexList returns the index of the first occurrence of the given string in the list or -1 130func IndexList(s string, list []string) int { 131 for i, l := range list { 132 if l == s { 133 return i 134 } 135 } 136 137 return -1 138} 139 140// InList checks if the string belongs to the list 141func InList(s string, list []string) bool { 142 return IndexList(s, list) != -1 143} 144 145func setFromList[T comparable](l []T) map[T]bool { 146 m := make(map[T]bool, len(l)) 147 for _, t := range l { 148 m[t] = true 149 } 150 return m 151} 152 153// ListSetDifference checks if the two lists contain the same elements. It returns 154// a boolean which is true if there is a difference, and then returns lists of elements 155// that are in l1 but not l2, and l2 but not l1. 156func ListSetDifference[T comparable](l1, l2 []T) (bool, []T, []T) { 157 listsDiffer := false 158 diff1 := []T{} 159 diff2 := []T{} 160 m1 := setFromList(l1) 161 m2 := setFromList(l2) 162 for t := range m1 { 163 if _, ok := m2[t]; !ok { 164 diff1 = append(diff1, t) 165 listsDiffer = true 166 } 167 } 168 for t := range m2 { 169 if _, ok := m1[t]; !ok { 170 diff2 = append(diff2, t) 171 listsDiffer = true 172 } 173 } 174 return listsDiffer, diff1, diff2 175} 176 177// Returns true if the given string s is prefixed with any string in the given prefix list. 178func HasAnyPrefix(s string, prefixList []string) bool { 179 for _, prefix := range prefixList { 180 if strings.HasPrefix(s, prefix) { 181 return true 182 } 183 } 184 return false 185} 186 187// Returns true if any string in the given list has the given substring. 188func SubstringInList(list []string, substr string) bool { 189 for _, s := range list { 190 if strings.Contains(s, substr) { 191 return true 192 } 193 } 194 return false 195} 196 197// Returns true if any string in the given list has the given prefix. 198func PrefixInList(list []string, prefix string) bool { 199 for _, s := range list { 200 if strings.HasPrefix(s, prefix) { 201 return true 202 } 203 } 204 return false 205} 206 207// Returns true if any string in the given list has the given suffix. 208func SuffixInList(list []string, suffix string) bool { 209 for _, s := range list { 210 if strings.HasSuffix(s, suffix) { 211 return true 212 } 213 } 214 return false 215} 216 217// IndexListPred returns the index of the element which in the given `list` satisfying the predicate, or -1 if there is no such element. 218func IndexListPred(pred func(s string) bool, list []string) int { 219 for i, l := range list { 220 if pred(l) { 221 return i 222 } 223 } 224 225 return -1 226} 227 228// FilterList divides the string list into two lists: one with the strings belonging 229// to the given filter list, and the other with the remaining ones 230func FilterList(list []string, filter []string) (remainder []string, filtered []string) { 231 // InList is O(n). May be worth using more efficient lookup for longer lists. 232 for _, l := range list { 233 if InList(l, filter) { 234 filtered = append(filtered, l) 235 } else { 236 remainder = append(remainder, l) 237 } 238 } 239 240 return 241} 242 243// FilterListPred returns the elements of the given list for which the predicate 244// returns true. Order is kept. 245func FilterListPred(list []string, pred func(s string) bool) (filtered []string) { 246 for _, l := range list { 247 if pred(l) { 248 filtered = append(filtered, l) 249 } 250 } 251 return 252} 253 254// RemoveListFromList removes the strings belonging to the filter list from the 255// given list and returns the result 256func RemoveListFromList(list []string, filter_out []string) (result []string) { 257 result = make([]string, 0, len(list)) 258 for _, l := range list { 259 if !InList(l, filter_out) { 260 result = append(result, l) 261 } 262 } 263 return 264} 265 266// RemoveFromList removes given string from the string list. 267func RemoveFromList(s string, list []string) (bool, []string) { 268 result := make([]string, 0, len(list)) 269 var removed bool 270 for _, item := range list { 271 if item != s { 272 result = append(result, item) 273 } else { 274 removed = true 275 } 276 } 277 return removed, result 278} 279 280// FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of 281// each. It modifies the slice contents in place, and returns a subslice of the original slice. 282func FirstUniqueStrings(list []string) []string { 283 // Do not moodify the input in-place, operate on a copy instead. 284 list = CopyOf(list) 285 // 128 was chosen based on BenchmarkFirstUniqueStrings results. 286 if len(list) > 128 { 287 return firstUniqueStringsMap(list) 288 } 289 return firstUniqueStringsList(list) 290} 291 292func firstUniqueStringsList(list []string) []string { 293 k := 0 294outer: 295 for i := 0; i < len(list); i++ { 296 for j := 0; j < k; j++ { 297 if list[i] == list[j] { 298 continue outer 299 } 300 } 301 list[k] = list[i] 302 k++ 303 } 304 return list[:k] 305} 306 307func firstUniqueStringsMap(list []string) []string { 308 k := 0 309 seen := make(map[string]bool, len(list)) 310 for i := 0; i < len(list); i++ { 311 if seen[list[i]] { 312 continue 313 } 314 seen[list[i]] = true 315 list[k] = list[i] 316 k++ 317 } 318 return list[:k] 319} 320 321// LastUniqueStrings returns all unique elements of a slice of strings, keeping the last copy of 322// each. It modifies the slice contents in place, and returns a subslice of the original slice. 323func LastUniqueStrings(list []string) []string { 324 totalSkip := 0 325 for i := len(list) - 1; i >= totalSkip; i-- { 326 skip := 0 327 for j := i - 1; j >= totalSkip; j-- { 328 if list[i] == list[j] { 329 skip++ 330 } else { 331 list[j+skip] = list[j] 332 } 333 } 334 totalSkip += skip 335 } 336 return list[totalSkip:] 337} 338 339// SortedUniqueStrings returns what the name says 340func SortedUniqueStrings(list []string) []string { 341 // FirstUniqueStrings creates a copy of `list`, so the input remains untouched. 342 unique := FirstUniqueStrings(list) 343 sort.Strings(unique) 344 return unique 345} 346 347// checkCalledFromInit panics if a Go package's init function is not on the 348// call stack. 349func checkCalledFromInit() { 350 for skip := 3; ; skip++ { 351 _, funcName, ok := callerName(skip) 352 if !ok { 353 panic("not called from an init func") 354 } 355 356 if funcName == "init" || strings.HasPrefix(funcName, "init·") || 357 strings.HasPrefix(funcName, "init.") { 358 return 359 } 360 } 361} 362 363// A regex to find a package path within a function name. It finds the shortest string that is 364// followed by '.' and doesn't have any '/'s left. 365var pkgPathRe = regexp.MustCompile(`^(.*?)\.([^/]+)$`) 366 367// callerName returns the package path and function name of the calling 368// function. The skip argument has the same meaning as the skip argument of 369// runtime.Callers. 370func callerName(skip int) (pkgPath, funcName string, ok bool) { 371 var pc [1]uintptr 372 n := runtime.Callers(skip+1, pc[:]) 373 if n != 1 { 374 return "", "", false 375 } 376 377 f := runtime.FuncForPC(pc[0]).Name() 378 s := pkgPathRe.FindStringSubmatch(f) 379 if len(s) < 3 { 380 panic(fmt.Errorf("failed to extract package path and function name from %q", f)) 381 } 382 383 return s[1], s[2], true 384} 385 386// GetNumericSdkVersion removes the first occurrence of system_ in a string, 387// which is assumed to be something like "system_1.2.3" 388func GetNumericSdkVersion(v string) string { 389 return strings.Replace(v, "system_", "", 1) 390} 391 392// copied from build/kati/strutil.go 393func substPattern(pat, repl, str string) string { 394 ps := strings.SplitN(pat, "%", 2) 395 if len(ps) != 2 { 396 if str == pat { 397 return repl 398 } 399 return str 400 } 401 in := str 402 trimmed := str 403 if ps[0] != "" { 404 trimmed = strings.TrimPrefix(in, ps[0]) 405 if trimmed == in { 406 return str 407 } 408 } 409 in = trimmed 410 if ps[1] != "" { 411 trimmed = strings.TrimSuffix(in, ps[1]) 412 if trimmed == in { 413 return str 414 } 415 } 416 417 rs := strings.SplitN(repl, "%", 2) 418 if len(rs) != 2 { 419 return repl 420 } 421 return rs[0] + trimmed + rs[1] 422} 423 424// copied from build/kati/strutil.go 425func matchPattern(pat, str string) bool { 426 i := strings.IndexByte(pat, '%') 427 if i < 0 { 428 return pat == str 429 } 430 return strings.HasPrefix(str, pat[:i]) && strings.HasSuffix(str, pat[i+1:]) 431} 432 433var shlibVersionPattern = regexp.MustCompile("(?:\\.\\d+(?:svn)?)+") 434 435// splitFileExt splits a file name into root, suffix and ext. root stands for the file name without 436// the file extension and the version number (e.g. "libexample"). suffix stands for the 437// concatenation of the file extension and the version number (e.g. ".so.1.0"). ext stands for the 438// file extension after the version numbers are trimmed (e.g. ".so"). 439func SplitFileExt(name string) (string, string, string) { 440 // Extract and trim the shared lib version number if the file name ends with dot digits. 441 suffix := "" 442 matches := shlibVersionPattern.FindAllStringIndex(name, -1) 443 if len(matches) > 0 { 444 lastMatch := matches[len(matches)-1] 445 if lastMatch[1] == len(name) { 446 suffix = name[lastMatch[0]:lastMatch[1]] 447 name = name[0:lastMatch[0]] 448 } 449 } 450 451 // Extract the file name root and the file extension. 452 ext := filepath.Ext(name) 453 root := strings.TrimSuffix(name, ext) 454 suffix = ext + suffix 455 456 return root, suffix, ext 457} 458 459// ShardPaths takes a Paths, and returns a slice of Paths where each one has at most shardSize paths. 460func ShardPaths(paths Paths, shardSize int) []Paths { 461 if len(paths) == 0 { 462 return nil 463 } 464 ret := make([]Paths, 0, (len(paths)+shardSize-1)/shardSize) 465 for len(paths) > shardSize { 466 ret = append(ret, paths[0:shardSize]) 467 paths = paths[shardSize:] 468 } 469 if len(paths) > 0 { 470 ret = append(ret, paths) 471 } 472 return ret 473} 474 475// ShardString takes a string and returns a slice of strings where the length of each one is 476// at most shardSize. 477func ShardString(s string, shardSize int) []string { 478 if len(s) == 0 { 479 return nil 480 } 481 ret := make([]string, 0, (len(s)+shardSize-1)/shardSize) 482 for len(s) > shardSize { 483 ret = append(ret, s[0:shardSize]) 484 s = s[shardSize:] 485 } 486 if len(s) > 0 { 487 ret = append(ret, s) 488 } 489 return ret 490} 491 492// ShardStrings takes a slice of strings, and returns a slice of slices of strings where each one has at most shardSize 493// elements. 494func ShardStrings(s []string, shardSize int) [][]string { 495 if len(s) == 0 { 496 return nil 497 } 498 ret := make([][]string, 0, (len(s)+shardSize-1)/shardSize) 499 for len(s) > shardSize { 500 ret = append(ret, s[0:shardSize]) 501 s = s[shardSize:] 502 } 503 if len(s) > 0 { 504 ret = append(ret, s) 505 } 506 return ret 507} 508 509// CheckDuplicate checks if there are duplicates in given string list. 510// If there are, it returns first such duplicate and true. 511func CheckDuplicate(values []string) (duplicate string, found bool) { 512 seen := make(map[string]string) 513 for _, v := range values { 514 if duplicate, found = seen[v]; found { 515 return duplicate, true 516 } 517 seen[v] = v 518 } 519 return "", false 520} 521