Skip to content

Commit baca793

Browse files
committed
Add bytesranger package
1 parent 2ca68f5 commit baca793

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package bytesranger
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
)
7+
8+
var (
9+
errZeroDivision = errors.New("bytesranger: zero division error")
10+
errNumberOfPartitions = errors.New("bytesranger: numberOfPartitions must be less than or equal to number")
11+
)
12+
13+
// Split splits the number into the specified numberOfPartitions and changes to bytes range strings.
14+
func Split(number int, numberOfPartitions int) ([]string, error) {
15+
err := validate(number, numberOfPartitions)
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
return toBytesRangeStrings(toBytesRanges(number, numberOfPartitions)), nil
21+
}
22+
23+
type bytesRange struct {
24+
first int
25+
last int
26+
}
27+
28+
func validate(number int, numberOfPartitions int) error {
29+
if numberOfPartitions == 0 {
30+
return errZeroDivision
31+
}
32+
33+
if number < numberOfPartitions {
34+
return errNumberOfPartitions
35+
}
36+
37+
return nil
38+
}
39+
40+
func toBytesRanges(number int, numberOfPartitions int) []bytesRange {
41+
ret := make([]bytesRange, 0)
42+
43+
numberPerUnit := number / numberOfPartitions
44+
45+
n := 0
46+
47+
for i := numberOfPartitions; i > 0; i-- {
48+
br := bytesRange{first: n, last: n + numberPerUnit - 1}
49+
ret = append(ret, br)
50+
n += numberPerUnit
51+
}
52+
53+
if rem := number % numberOfPartitions; rem != 0 {
54+
ret[len(ret)-1].last += rem
55+
}
56+
57+
return ret
58+
}
59+
60+
func toBytesRangeStrings(bytesRanges []bytesRange) []string {
61+
ret := make([]string, 0)
62+
63+
for _, br := range bytesRanges {
64+
ret = append(ret, fmt.Sprintf("bytes=%d-%d", br.first, br.last))
65+
}
66+
67+
return ret
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package bytesranger
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestBytesranger_Split(t *testing.T) {
9+
cases := map[string]struct {
10+
number int
11+
numberOfPartitions int
12+
expected []string
13+
}{
14+
"split 5 into 1": {number: 5, numberOfPartitions: 1, expected: []string{"bytes=0-4"}},
15+
"split 5 into 2": {number: 5, numberOfPartitions: 2, expected: []string{"bytes=0-1", "bytes=2-4"}},
16+
"split 5 into 3": {number: 5, numberOfPartitions: 3, expected: []string{"bytes=0-0", "bytes=1-1", "bytes=2-4"}},
17+
"split 5 into 4": {number: 5, numberOfPartitions: 4, expected: []string{"bytes=0-0", "bytes=1-1", "bytes=2-2", "bytes=3-4"}},
18+
"split 5 into 5": {number: 5, numberOfPartitions: 5, expected: []string{"bytes=0-0", "bytes=1-1", "bytes=2-2", "bytes=3-3", "bytes=4-4"}},
19+
}
20+
21+
for n, c := range cases {
22+
c := c
23+
t.Run(n, func(t *testing.T) {
24+
t.Parallel()
25+
26+
number := c.number
27+
numberOfPartitions := c.numberOfPartitions
28+
expected := c.expected
29+
30+
actual, err := Split(number, numberOfPartitions)
31+
if err != nil {
32+
t.Fatalf("err %s", err)
33+
}
34+
if !reflect.DeepEqual(actual, expected) {
35+
t.Errorf(`expected="%s" actual="%s"`, expected, actual)
36+
}
37+
})
38+
}
39+
}
40+
41+
func TestBytesranger_Split_ZeroDivisionError(t *testing.T) {
42+
actual, err := Split(10, 0)
43+
if len(actual) != 0 {
44+
t.Fatalf("expected that length is 0, but %d", len(actual))
45+
}
46+
if err != errZeroDivision {
47+
t.Errorf(`expected="%v" actual="%v"`, errZeroDivision, err)
48+
}
49+
}
50+
51+
func TestBytesranger_Split_NumberOfPartitionsError(t *testing.T) {
52+
actual, err := Split(10, 11)
53+
if len(actual) != 0 {
54+
t.Fatalf("expected that length is 0, but %d", len(actual))
55+
}
56+
if err != errNumberOfPartitions {
57+
t.Errorf(`expected="%v" actual="%v"`, errNumberOfPartitions, err)
58+
}
59+
}

0 commit comments

Comments
 (0)