File: ranges.go

package info (click to toggle)
golang-github-mesos-mesos-go 0.0.6%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 11,724 kB
  • sloc: makefile: 163
file content (253 lines) | stat: -rw-r--r-- 6,733 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
package mesos

import (
	"sort"
)

// Ranges represents a list of Ranges.
type Ranges []Value_Range

// NewRanges returns squashed Ranges from the given numbers.
func NewRanges(ns ...uint64) Ranges {
	xs := append(uint64s{}, ns...)
	sort.Sort(xs)
	rs := make(Ranges, len(xs))
	for i := range xs {
		rs[i].Begin, rs[i].End = xs[i], xs[i]
	}
	return rs.Squash()
}

// NewPortRanges returns Ranges from the "ports" resource in the
// given *Offer. If that resource isn't provided, nil will be returned.
//
// The returned Ranges are sorted and have all overlapping ranges merged from
// left to right. e.g. [[0, 5], [4, 3], [10, 7]] -> [[0, 5], [7, 10]]
func NewPortRanges(o *Offer) Ranges {
	if o == nil {
		return Ranges{}
	}

	var (
		r     Resource
		found bool
	)
	for i := range o.Resources {
		if o.Resources[i].GetName() == "ports" {
			r = o.Resources[i]
			found = true
			break
		}
	}

	if !found {
		return Ranges{}
	}

	offered := r.GetRanges().GetRange()
	rs := make(Ranges, len(offered))
	for i, r := range offered {
		if lo, hi := r.GetBegin(), r.GetEnd(); lo <= hi {
			rs[i].Begin, rs[i].End = lo, hi
		} else {
			rs[i].Begin, rs[i].End = hi, lo
		}
	}
	return rs.Sort().Squash()
}

// These three methods implement sort.Interface
func (rs Ranges) Len() int      { return len(rs) }
func (rs Ranges) Swap(i, j int) { rs[i], rs[j] = rs[j], rs[i] }
func (rs Ranges) Less(i, j int) bool {
	return rs[i].Begin < rs[j].Begin || (rs[i].Begin == rs[j].Begin && rs[i].End < rs[j].End)
}

// Size returns the sum of the Size of all Ranges.
func (rs Ranges) Size() uint64 {
	var sz uint64
	for i := range rs {
		sz += 1 + (rs[i].End - rs[i].Begin)
	}
	return sz
}

// Sort sorts the receiving Ranges and returns the result; convenience
func (rs Ranges) Sort() Ranges {
	sort.Sort(rs)
	return rs
}

// Squash merges overlapping and continuous Ranges. It assumes they're pre-sorted.
func (rs Ranges) Squash() Ranges {
	if len(rs) < 2 {
		return rs
	}
	squashed := Ranges{rs[0]}
	for i := 1; i < len(rs); i++ {
		switch max := squashed[len(squashed)-1].End; {
		case 1+max < rs[i].Begin: // no overlap nor continuity: push
			squashed = append(squashed, rs[i])
		case max <= rs[i].End: // overlap or continuity: squash
			squashed[len(squashed)-1].End = rs[i].End
		}
	}
	return squashed
}

// Search performs a binary search for n returning the index of the Range it was
// found at or -1 if not found.
func (rs Ranges) Search(n uint64) int {
	for lo, hi := 0, len(rs)-1; lo <= hi; {
		switch m := lo + (hi-lo)/2; {
		case n < rs[m].Begin:
			hi = m - 1
		case n > rs[m].End:
			lo = m + 1
		default:
			return m
		}
	}
	return -1
}

// Partition partitions Ranges around n. It returns the partitioned Ranges
// and a boolean indicating if n was found.
func (rs Ranges) Partition(n uint64) (Ranges, bool) {
	i := rs.Search(n)
	if i < 0 {
		return rs, false
	}

	pn := make(Ranges, 0, len(rs)+1)
	switch pn = append(pn, rs[:i]...); {
	case rs[i].Begin == rs[i].End: // delete
	case rs[i].Begin == n: // increment lower bound
		pn = append(pn, Value_Range{rs[i].Begin + 1, rs[i].End})
	case rs[i].End == n: // decrement upper bound
		pn = append(pn, Value_Range{rs[i].Begin, rs[i].End - 1})
	default: // split
		pn = append(pn, Value_Range{rs[i].Begin, n - 1}, Value_Range{n + 1, rs[i].End})
	}
	return append(pn, rs[i+1:]...), true
}

// Remove removes a range from already coalesced ranges.
// The algorithms constructs a new vector of ranges which is then
// Squash'ed into a Ranges instance.
func (rs Ranges) Remove(removal Value_Range) Ranges {
	ranges := make([]Value_Range, 0, len(rs))
	for _, r := range rs {
		// skip if the entire range is subsumed by removal
		if r.Begin >= removal.Begin && r.End <= removal.End {
			continue
		}
		// divide if the range subsumes the removal
		if r.Begin < removal.Begin && r.End > removal.End {
			ranges = append(ranges,
				Value_Range{r.Begin, removal.Begin - 1},
				Value_Range{removal.End + 1, r.End},
			)
			continue
		}
		// add the full range if there's no intersection
		if r.End < removal.Begin || r.Begin > removal.End {
			ranges = append(ranges, r)
			continue
		}
		// trim if the range does intersect
		if r.End > removal.End {
			ranges = append(ranges, Value_Range{removal.End + 1, r.End})
		} else {
			if r.Begin >= removal.Begin {
				// should never happen
				panic("r.Begin >= removal.Begin")
			}
			ranges = append(ranges, Value_Range{r.Begin, removal.Begin - 1})
		}
	}
	return Ranges(ranges).Squash()
}

// Compare assumes that both Ranges are already in sort-order.
// Returns 0 if rs and right are equivalent, -1 if rs is a subset of right, or else 1
func (rs Ranges) Compare(right Ranges) int {
	x, y, result := rs.equiv(right)
	if result {
		return 0
	}
	for _, a := range x {
		// make sure that this range is a subset of a range in y
		matched := false
		for _, b := range y {
			if a.Begin >= b.Begin && a.End <= b.End {
				matched = true
				break
			}
		}
		if !matched {
			return 1
		}
	}
	return -1
}

// Equivalent assumes that both Ranges are already in sort-order.
func (rs Ranges) Equivalent(right Ranges) (result bool) {
	_, _, result = rs.equiv(right)
	return
}

// Equivalent assumes that both Ranges are already in sort-order.
func (rs Ranges) equiv(right Ranges) (_, _ Ranges, _ bool) {
	// we need to squash rs and right but don't want to change the originals
	switch len(rs) {
	case 0:
	case 1:
		rs = Ranges{rs[0]}
	default:
		rs = Ranges(append([]Value_Range{rs[0], rs[1]}, rs[2:]...)).Sort().Squash()
	}
	switch len(right) {
	case 0:
	case 1:
		right = Ranges{right[0]}
	default:
		right = Ranges(append([]Value_Range{right[0], right[1]}, right[2:]...)).Sort().Squash()
	}
	return rs, right, (&Value_Ranges{Range: rs}).Equal(&Value_Ranges{Range: right})
}

func (rs Ranges) Clone() Ranges {
	if len(rs) == 0 {
		return nil
	}
	x := make(Ranges, len(rs))
	copy(x, rs)
	return x
}

// Min returns the minimum number in Ranges. It will panic on empty Ranges.
func (rs Ranges) Min() uint64 { return rs[0].Begin }

// Max returns the maximum number in Ranges. It will panic on empty Ranges.
func (rs Ranges) Max() uint64 { return rs[len(rs)-1].End }

// resource returns a *Resource with the given name and Ranges.
func (rs Ranges) resource(name string) Resource {
	vr := make([]Value_Range, len(rs))
	copy(vr, rs)
	return Resource{
		Name:   name,
		Type:   RANGES.Enum(),
		Ranges: &Value_Ranges{Range: vr},
	}
}

// uint64s is an utility used to sort a slice of uint64s
type uint64s []uint64

// These three methods implement sort.Interface
func (ns uint64s) Len() int           { return len(ns) }
func (ns uint64s) Less(i, j int) bool { return ns[i] < ns[j] }
func (ns uint64s) Swap(i, j int)      { ns[i], ns[j] = ns[j], ns[i] }