github.com/goern/docker@v1.9.0-rc1/pkg/nat/sort.go (about)

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