diff --git a/utils/upload/upload.go b/utils/upload/upload.go new file mode 100644 index 00000000..c7447b54 --- /dev/null +++ b/utils/upload/upload.go @@ -0,0 +1,27 @@ +package upload + +import ( + "iter" + + "golang.org/x/exp/constraints" +) + +type Chunk[I constraints.Integer] struct { + Offset I + Size I +} + +// Chunks yields a sequence of a part number and a Chunk. The Chunk is the offset +// and size of the chunk. The last chunk may be smaller than chunkSize if size is +// not a multiple of chunkSize. +// +// The first part number is 1 and increases monotonically. +func Chunks[I constraints.Integer](size, chunkSize I) iter.Seq2[int, Chunk[I]] { + return func(yield func(int, Chunk[I]) bool) { + var n int + for off := I(0); off < size; off += chunkSize { + n++ + yield(n, Chunk[I]{off, min(chunkSize, size-off)}) + } + } +} diff --git a/utils/upload/upload_test.go b/utils/upload/upload_test.go new file mode 100644 index 00000000..44ad7f21 --- /dev/null +++ b/utils/upload/upload_test.go @@ -0,0 +1,37 @@ +package upload + +import ( + "testing" + + "kr.dev/diff" +) + +func TestChunks(t *testing.T) { + const size = 101 + const chunkSize = 10 + var got []Chunk[int] + var lastN int + for n, c := range Chunks(size, chunkSize) { + if n != lastN+1 { + t.Errorf("n = %d; want %d", n, lastN+1) + } + got = append(got, c) + lastN = n + } + + want := []Chunk[int]{ + {0, 10}, + {10, 10}, + {20, 10}, + {30, 10}, + {40, 10}, + {50, 10}, + {60, 10}, + {70, 10}, + {80, 10}, + {90, 10}, + {100, 1}, + } + + diff.Test(t, t.Errorf, got, want) +}