github.com/torfuzx/docker@v1.8.1/pkg/nat/sort.go (about)

     1  package nat
     2  
     3  import (
     4  	"sort"
     5  	"strconv"
     6  	"strings"
     7  )
     8  
     9  type portSorter struct {
    10  	ports []Port
    11  	by    func(i, j Port) bool
    12  }
    13  
    14  func (s *portSorter) Len() int {
    15  	return len(s.ports)
    16  }
    17  
    18  func (s *portSorter) Swap(i, j int) {
    19  	s.ports[i], s.ports[j] = s.ports[j], s.ports[i]
    20  }
    21  
    22  func (s *portSorter) Less(i, j int) bool {
    23  	ip := s.ports[i]
    24  	jp := s.ports[j]
    25  
    26  	return s.by(ip, jp)
    27  }
    28  
    29  // Sort sorts a list of ports using the provided predicate
    30  // This function should compare `i` and `j`, returning true if `i` is
    31  // considered to be less than `j`
    32  func Sort(ports []Port, predicate func(i, j Port) bool) {
    33  	s := &portSorter{ports, predicate}
    34  	sort.Sort(s)
    35  }
    36  
    37  type portMapEntry struct {
    38  	port    Port
    39  	binding PortBinding
    40  }
    41  
    42  type portMapSorter []portMapEntry
    43  
    44  func (s portMapSorter) Len() int      { return len(s) }
    45  func (s portMapSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
    46  
    47  // sort the port so that the order is:
    48  // 1. port with larger specified bindings
    49  // 2. larger port
    50  // 3. port with tcp protocol
    51  func (s portMapSorter) Less(i, j int) bool {
    52  	pi, pj := s[i].port, s[j].port
    53  	hpi, hpj := toInt(s[i].binding.HostPort), toInt(s[j].binding.HostPort)
    54  	return hpi > hpj || pi.Int() > pj.Int() || (pi.Int() == pj.Int() && strings.ToLower(pi.Proto()) == "tcp")
    55  }
    56  
    57  // SortPortMap sorts the list of ports and their respected mapping. The ports
    58  // will explicit HostPort will be placed first.
    59  func SortPortMap(ports []Port, bindings PortMap) {
    60  	s := portMapSorter{}
    61  	for _, p := range ports {
    62  		if binding, ok := bindings[p]; ok {
    63  			for _, b := range binding {
    64  				s = append(s, portMapEntry{port: p, binding: b})
    65  			}
    66  			bindings[p] = []PortBinding{}
    67  		} else {
    68  			s = append(s, portMapEntry{port: p})
    69  		}
    70  	}
    71  
    72  	sort.Sort(s)
    73  	var (
    74  		i  int
    75  		pm = make(map[Port]struct{})
    76  	)
    77  	// reorder ports
    78  	for _, entry := range s {
    79  		if _, ok := pm[entry.port]; !ok {
    80  			ports[i] = entry.port
    81  			pm[entry.port] = struct{}{}
    82  			i++
    83  		}
    84  		// reorder bindings for this port
    85  		if _, ok := bindings[entry.port]; ok {
    86  			bindings[entry.port] = append(bindings[entry.port], entry.binding)
    87  		}
    88  	}
    89  }
    90  
    91  func toInt(s string) int64 {
    92  	i, err := strconv.ParseInt(s, 10, 64)
    93  	if err != nil {
    94  		i = 0
    95  	}
    96  	return i
    97  }