From 30ec3c606d41ba687fb6916992621571b7fab757 Mon Sep 17 00:00:00 2001 From: Avraham Shalev <8184528+avrahams@users.noreply.github.com> Date: Sun, 26 Jun 2022 11:26:56 +0300 Subject: [PATCH] Add user friendly wrapper to split2SliceChunks --- httputils/httphelper_test.go | 10 ++-------- httputils/httphelpers.go | 26 +++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/httputils/httphelper_test.go b/httputils/httphelper_test.go index 1bcddaf..63f895f 100644 --- a/httputils/httphelper_test.go +++ b/httputils/httphelper_test.go @@ -131,13 +131,7 @@ func TestSplitSlice2Chunks(t *testing.T) { } //split splice to chunks - chunksChan := make(chan []testStruct, 10) - wg := sync.WaitGroup{} - SplitSlice2Chunks(testTypeSlice, 100, chunksChan, &wg) - go func() { - wg.Wait() - close(chunksChan) - }() + chunksChan, totalTestTypes := SplitSlice2Chunks(testTypeSlice, 100, 10) testWg := sync.WaitGroup{} var totalReceived, numOfChunks, maxChunkSize, minChunkSize, maxChunkLength, minChunkLength int testWg.Add(1) @@ -166,7 +160,7 @@ func TestSplitSlice2Chunks(t *testing.T) { //wait for all chunks to arrive testWg.Wait() //compare with expected - assert.Equal(t, len(testTypeSlice), totalReceived, "total elements received is not equal to number of element sent") + assert.Equal(t, totalTestTypes, totalReceived, "total elements received is not equal to number of element sent") assert.Equal(t, 3, minChunkLength, "minChunkLength must be same as expected minChunkLength") assert.Equal(t, 3, maxChunkLength, "maxChunkLength must be same as expected maxChunkLength") assert.Equal(t, 77, minChunkSize, "minChunkSize must be same as expected minChunkSize") diff --git a/httputils/httphelpers.go b/httputils/httphelpers.go index 036f939..069677e 100644 --- a/httputils/httphelpers.go +++ b/httputils/httphelpers.go @@ -90,10 +90,30 @@ func HttpRespToString(resp *http.Response) (string, error) { } //SplitSlice2Chunks - *recursively* splits a slice to chunks of sub slices that do not exceed max bytes size +//Returns a channels for receiving []T chunks and the original len of []T +//If []T is empty the function will return a closed chunks channel //Chunks might be bigger than max size if the slice contains element(s) that are bigger than the max size //this split algorithm fits for slices with elements that share more or less the same size per element //uses optimistic average size splitting to enhance performance and reduce the use of json encoding for size calculations -func SplitSlice2Chunks[T any](slice []T, maxSize int, chunks chan<- []T, wg *sync.WaitGroup) { +//chunks channel will be closed after splitting is done +func SplitSlice2Chunks[T any](slice []T, maxSize int, channelBuffer int) (chunksChannel <-chan []T, sliceSize int) { + channel := make(chan []T, channelBuffer) + sliceSize = len(slice) + if sliceSize > 0 { + go func(chunksChannel chan<- []T) { + splitWg := &sync.WaitGroup{} + splitSlice2Chunks(slice, maxSize, chunksChannel, splitWg) + splitWg.Wait() + close(chunksChannel) + }(channel) + } else { + close(channel) + } + chunksChannel = channel + return chunksChannel, sliceSize +} + +func splitSlice2Chunks[T any](slice []T, maxSize int, chunks chan<- []T, wg *sync.WaitGroup) { wg.Add(1) go func(slice []T, maxSize int, chunks chan<- []T, wg *sync.WaitGroup) { defer wg.Done() @@ -124,11 +144,11 @@ func SplitSlice2Chunks[T any](slice []T, maxSize int, chunks chan<- []T, wg *syn //split the slice to slices of avgSliceSize size startIndex := 0 for i := avgSliceSize; i < last; i += avgSliceSize { - SplitSlice2Chunks(slice[startIndex:i], maxSize, chunks, wg) + splitSlice2Chunks(slice[startIndex:i], maxSize, chunks, wg) startIndex = i } //send the last part of the slice - SplitSlice2Chunks(slice[startIndex:last], maxSize, chunks, wg) + splitSlice2Chunks(slice[startIndex:last], maxSize, chunks, wg) }(slice, maxSize, chunks, wg) }