registry/storage: more efficient path compare in catalog
Previous component-wise path comparison is recursive and generates a large amount of garbage. This more efficient version simply replaces the path comparison with the zero-value to sort before everything. We do this by replacing the byte-wise comparison that swaps a single character inline for the separator comparison, such that separators sort first. The resulting implementation provides component-wise path comparison with no cost incurred for allocation or stack frame. Direction of the comparison is also reversed to match Go style. Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
		
							parent
							
								
									bfa0a9c097
								
							
						
					
					
						commit
						bba5a0d05c
					
				
					 1 changed files with 43 additions and 16 deletions
				
			
		|  | @ -39,7 +39,7 @@ func (reg *registry) Repositories(ctx context.Context, repos []string, last stri | |||
| 		_, file := path.Split(repoPath) | ||||
| 		if file == "_layers" { | ||||
| 			repoPath = strings.TrimSuffix(repoPath, "/_layers") | ||||
| 			if pathGreaterThan(repoPath, last) { | ||||
| 			if lessPath(last, repoPath) { | ||||
| 				foundRepos = append(foundRepos, repoPath) | ||||
| 			} | ||||
| 			return ErrSkipDir | ||||
|  | @ -96,22 +96,49 @@ func (reg *registry) Enumerate(ctx context.Context, ingester func(string) error) | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| func pathGreaterThan(pathX, pathY string) (b bool) { | ||||
| 	splitPathX := strings.SplitN(pathX, "/", 2) | ||||
| 	splitPathY := strings.SplitN(pathY, "/", 2) | ||||
| 
 | ||||
| 	if splitPathX[0] == splitPathY[0] { | ||||
| 		if len(splitPathX) == 1 && len(splitPathY) == 1 { | ||||
| 			return false | ||||
| 		} else if len(splitPathX) == 1 && len(splitPathY) != 1 { | ||||
| 			return false | ||||
| 		} else if len(splitPathX) != 1 && len(splitPathY) == 1 { | ||||
| 			return true | ||||
| 		} | ||||
| 
 | ||||
| 		return pathGreaterThan(splitPathX[1], splitPathY[1]) | ||||
| // lessPath returns true if one path a is less than path b. | ||||
| // | ||||
| // A component-wise comparison is done, rather than the lexical comparison of | ||||
| // strings. | ||||
| func lessPath(a, b string) bool { | ||||
| 	// we provide this behavior by making separator always sort first. | ||||
| 	return compareReplaceInline(a, b, '/', '\x00') < 0 | ||||
| } | ||||
| 
 | ||||
| // compareReplaceInline modifies runtime.cmpstring to replace old with new | ||||
| // during a byte-wise comparison. | ||||
| func compareReplaceInline(s1, s2 string, old, new byte) int { | ||||
| 	l := len(s1) | ||||
| 	if len(s2) < l { | ||||
| 		l = len(s2) | ||||
| 	} | ||||
| 
 | ||||
| 	return splitPathX[0] > splitPathY[0] | ||||
| 	for i := 0; i < l; i++ { | ||||
| 		c1, c2 := s1[i], s2[i] | ||||
| 		if c1 == old { | ||||
| 			c1 = new | ||||
| 		} | ||||
| 
 | ||||
| 		if c2 == old { | ||||
| 			c2 = new | ||||
| 		} | ||||
| 
 | ||||
| 		if c1 < c2 { | ||||
| 			return -1 | ||||
| 		} | ||||
| 
 | ||||
| 		if c1 > c2 { | ||||
| 			return +1 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if len(s1) < len(s2) { | ||||
| 		return -1 | ||||
| 	} | ||||
| 
 | ||||
| 	if len(s1) > len(s2) { | ||||
| 		return +1 | ||||
| 	} | ||||
| 
 | ||||
| 	return 0 | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue