ImHex icon indicating copy to clipboard operation
ImHex copied to clipboard

[Feature] Access the last item in the condition of a loop-sized array

Open AlexGuo1998 opened this issue 5 months ago • 4 comments

What feature would you like to see?

I want to access the last / previous item, to check if a loop-sized array should end.

It's better to make the whole condition evaluates to true when the last item doesn't exist. (Although I can work around if it throws an error)

For example:

// This is of variable size...
struct Block {
    u8 is_final;
    u16 size;
    u8 data[size];
};

struct Blocks {
    // The array has at least 1 item, and ends when the `is_final` flag in the last item is set
    Block items[while(<last_item>.is_final == 0 )];  // <- here
};

How will this feature be useful to you and others?

To be able to parse structures signaling their ending with a flag more easily.

Request Type

  • [ ] I can provide a PoC for this feature or am willing to work on it myself and submit a PR

Additional context?

There is a (somewhat) workaround, which needs to split the last item:

struct Blocks {
    Block items[while(std::mem::read_unsigned($, 1) == 0)];
    Block last_item;
};

... but it's still not ideal. It works only when the flag is not after some "dynamic" items.


I also tried to use this very complicated syntax but without luck, because items is not yet available in the while(...) block.

struct Blocks {
    u64 _start = $;
    Block items[while(
        $ == _start
        || (items[std::core::member_count(items) - 1].is_final == 0)
    )];
};

Might also be able to write a function to pre-calculate the item count, but this will duplicate the parse logic (once in the function and once in the struct).

AlexGuo1998 avatar Jun 19 '25 11:06 AlexGuo1998

this is what I would do instead:

// This is of variable size...
struct Block {
    u16 size;
    u8 data[size];
    u8 is_final;
    if (is_final)
          break;
};

struct Blocks {
    // The array has at least 1 item, and ends when the `is_final` flag in the last item is set
    u8 is_final;
    if (!is_final)
        Block items[while(true)];  
};

this will work even if the first is_final is true in which case items would not be defined. It also avoids creating an empty element at the end. You can also use continue to skip the last element while setting a global flag in a more general solution where the check is made somewhere in the middle of the struct.

bool read_more = true;
// This is of variable size...
struct Block {
    u16 size;
    u8 data[size];
    u8 is_final;
    if (last_one) {
         read_more = false; // this will make it not read the next element.
         continue;  //this deletes all data read so far for this element but continues reading
    }
    u8 more_data[size];
};

struct Blocks {
    // The array has at least 1 item, and ends when the `is_final` flag in the last item is set
     Block items[while(read_more)];  
};

This should accomplish what the feature is requesting without having to add more syntax and can be used using the current code.

paxcut avatar Jun 19 '25 11:06 paxcut

Ah you can use break to stop an array from growing. Never thought of this and thanks.

BTW, I need to create that (potentially) empty element and add it to the array, so this is what I used:

// This is of variable size...
struct Block {
    u8 is_final;
    u16 size;
    u8 data[size];

    if (is_final) break;
};

Could be better to add a note in the documentation, at the array part. https://github.com/WerWolv/Documentation/blob/master/pattern_language/core-language/data-types.md#arrays

AlexGuo1998 avatar Jun 19 '25 11:06 AlexGuo1998

The documentation for the flow control instructions to use in arrays can be found here

paxcut avatar Jun 19 '25 11:06 paxcut

I'm not sure if this should be a separate issue because it mostly duplicates the request from @AlexGuo1998, so I'll add my two cents here.

It would be nice to have an until condition and a way to access the last item in the predicate for a loop-sized array. Something like kaitai-struct repeat-until. IMHO, the current way of representing such situations is too verbose and difficult to follow because the repetition logic is distributed between the array and struct declarations. Although control flow instructions are flexible, it would be helpful to be able to represent common repetition patterns using loop-sized arrays.

rvost avatar Sep 16 '25 18:09 rvost