Rice
Rice

Reputation: 3531

Expanding a slice's size to prevent slice bounds out of range error

I have written the following:

func main() {
    //inside main        
        fileInputBytes, err := ioutil.ReadFile("/tmp/test")
        byteSize2 := len(fileInputBytes)

        var inputFileByteSlice = fileInputBytes[0:]
        var numberOfIndexes = math.Floor(float64(byteSize / indexingOffset))

        for i := 1; i <= int(numberOfIndexes); i++ {
            // adding i to the indexer insures that we use lookahed to ignore previously inserted indexing values
            var v int = (i * indexingOffset) + i
            Insert(&inputFileByteSlice, v+i, indexingByteValue)
            fmt.Println(i)
        }
    }
    //outside main
    //variation of https://blog.golang.org/slices with pointers and such
        func Insert(slice *[]byte, index int, value byte) {
            // Grow the slice by one element.
            (*slice) = (*slice)[0 : len(*slice)+1]
            // Use copy to move the upper part of the slice out of the way and open a hole.
            copy((*slice)[index+1:], (*slice)[index:])
            // Store the new value.
            (*slice)[index] = value
            // Return the result.
        }

The slice bounds out of range error is getting on my nerves. The length of the slice grows outside of the size and overflows, the reason I don't understand is that I thought the call to 'grow' the slice by one element(before copy) will dynamically allocate more space. Since that is not the case, can anyone offer me a better suggestion?

Upvotes: 2

Views: 993

Answers (2)

Mr_Pink
Mr_Pink

Reputation: 109357

Your function only works if the slice happens to have enough initial capacity. If you need more capacity, you can only "grow" the slice using the append function. You can still use the *[]byte pointer argument to modify the slice in place like so:

func Insert(slice *[]byte, index int, value byte) {
    *slice = append(*slice, 0)
    copy((*slice)[index+1:], (*slice)[index:])
    (*slice)[index] = value
}

However, it's more customary to return a new slice value, and reassign it each time. This gives you a similar function signature to the builtin append.

func Insert(slice []byte, index int, value byte) []byte {
    slice = append(slice, 0)
    copy(slice[index+1:], slice[index:])
    slice[index] = value
    return slice
}

Upvotes: 1

abhink
abhink

Reputation: 9126

First of all, a slice is already a reference type. So you don't need to pass its pointer around if you are not going to change its capacity. So your main can be simplified as:

func main() {
    fileInputBytes, err := ioutil.ReadFile("/tmp/test")
    byteSize2 := len(fileInputBytes)

    // No need to use pointer to slice. If you want a brand new slice
    // that does not affect the original slice values, use copy()
    inputFileByteArray := fileInputBytes
    var numberOfIndexes = math.Floor(float64(byteSize / indexingOffset))

    for i := 1; i <= int(numberOfIndexes); i++ {
        var v int = (i * indexingOffset) + i

        // Insert needs to return the newly updated slice reference
        // which should be assigned in each iteration.
        inputFileByteArray = Insert(inputFileByteArray, v+i, indexingByteValue)
        fmt.Println(i)
    }
}

Then, the Insert function can be simplified simply by using append along with copy and returning the newly created slice:

func Insert(slice []byte, index int, value byte) []byte {
    if index >= len(slice) {
        // add to the end of slice in case of index >= len(slice)
        return append(slice, value)
    }
    tmp := make([]byte, len(slice[:index + 1]))
    copy(tmp, slice[:index])
    tmp[index] = value
    return append(tmp, slice[index:]...)
}

This may not be the best implementation but it is simple enough. Example usage at: https://play.golang.org/p/Nuq4RX9XQD

Upvotes: 2

Related Questions