Repro case for "Array out of Bounds" problem

For a long time, I’ve been getting intermittent “Array out of bounds” errors in the IDE, when I’m very certain that there is no indexing error. Other people have mentioned it from time to time. It comes and goes, and I usually just work around it and forget about it. I’ve finally found a reproducible case. (firmware 2.23) It’s not critical, but it is useful to know about.

Here’s the failure, adapted from the “Sparks” pattern:

and here it is “fixed”. (I changed the i < numSparks to the equivalent expression i <= (numSparks - 1) )

Here’s code for the “broken” pattern:

var numSparks = 1 + (pixelCount / 10);
var sparks = array(numSparks);

export function beforeRender(delta) {

  for (var i = 0; i < numSparks; i++) {
    if (sparks[i] >= 0) {
      // do nothing
    }
  }
}

export function render(index) {
  hsv(0,0,0)
}

Here is probably what is going on. In languages like python you strictly need an integer as an array index. But here in pixel blaze land all numbers are 16.16 fixed point. What is likely happening is that numsparks is a non integer value, something like A.B, but when the array is created , the value is just truncated to A integer elements. When you run your for loop you are using the full A.B value, which can run an additional cycle because the < check passes.

Here is a trivial example with the variables exported.

2 Likes

That makes total sense, and is a very good explanation!

It explains why, after hooking up a long LED string that resulted in a non-integral numSparks, I could never again save or modify the Fireflies pattern. (It was one of the preloaded patterns at the time – I just assumed it knew what it was doing.) And it serves as a warning to be careful about introducing unintended strip length dependencies into your patterns.

Thanks!

@spazzle got it!

Just to expand a bit:

Let’s say you have 99 pixels, numSparks will be 10.9. When this is interpreted as an integer to array(), its fractional bits truncated to 10, and your array is now 10 elements large.

This expression remains true when i is 10 and numSparks is 10.9:

i < numSparks

So the loop does not break/exit and continues to execute for the 11th iteration. However there is no index 10 in an array that is only 10 elements, so sparks[10] is out of bounds.

The fix is to use the floor (round down) or ceil (round up) functions when you need to work with integers and do not want the fractional value. Using pixelCount to adjust patterns is encouraged! The original sparks pattern just hard codes 20, which doesn’t scale at all. You’d also want to do the same thing if you used a slider to pick the number of sparks, after scaling up to some number, you’d need to round it to some integer value.

Here’s the fixed version (though it’s still rendering black, no array index issues):

var numSparks = floor(1 + (pixelCount / 10));
var sparks = array(numSparks);

export function beforeRender(delta) {

  for (var i = 0; i < numSparks; i++) {
    if (sparks[i] >= 0) {
      // do nothing
    }
  }
}

export function render(index) {
  hsv(0,0,0)
}
1 Like

Probably won’t be the last time I get bitten by working in one language while leaving my brain behind in another!

I use Pixelblaze’s automatic truncation of 16.16 indices in virtually every pattern I write. It’s extremely handy in about a dozen ways. After a day of C++, I somehow manufactured an int numSparks = ... declaration from that code and just knew that calculations done at initialization would be automatically truncated because… you know… int, right?

Nope. Totally my bad! Thanks for the catch @spazzle!