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