plcrash_error_t plcrash_async_cfe_register_decode()

in Source/PLCrashAsyncCompactUnwindEncoding.c [503:583]


plcrash_error_t plcrash_async_cfe_register_decode (uint32_t permutation, uint32_t count, uint32_t registers[]) {
    /* Validate that count falls within the supported range */
    if (count > PLCRASH_ASYNC_CFE_PERMUTATION_REGISTER_MAX) {
        PLCF_DEBUG("Register permutation decoding attempted with an unsupported count of %" PRIu32, count);
        return PLCRASH_EINVAL;
    }

    /*
     * Each register is encoded by mapping the values to a 10-bit range, and then further sub-ranges within that range,
     * with a subrange allocated to each position. See the encoding function for full documentation.
     */
    int permunreg[PLCRASH_ASYNC_CFE_PERMUTATION_REGISTER_MAX];
#define PERMUTE(pos, factor) do { \
permunreg[pos] = permutation/factor; \
permutation -= (permunreg[pos]*factor); \
} while (0)

    /* Assert that the maximum register count matches our switch() statement. */
    PLCR_ASSERT_STATIC(expected_max_register_count, PLCRASH_ASYNC_CFE_PERMUTATION_REGISTER_MAX == 6);
	switch (count) {
		case 6:
            PERMUTE(0, 120);
            PERMUTE(1, 24);
            PERMUTE(2, 6);
            PERMUTE(3, 2);
            PERMUTE(4, 1);
            
            /*
             * There are 6 elements in the list, 6 possible values for each element, and values may not repeat. The
             * value of the last element can be derived from the values previously seen (and due to the positional
             * renumbering performed, the value of the last element will *always* be 0).
             */
            permunreg[5] = 0;
			break;
		case 5:
            PERMUTE(0, 120);
            PERMUTE(1, 24);
            PERMUTE(2, 6);
            PERMUTE(3, 2);
            PERMUTE(4, 1);
			break;
		case 4:
            PERMUTE(0, 60);
            PERMUTE(1, 12);
            PERMUTE(2, 3);
            PERMUTE(3, 1);
			break;
		case 3:
            PERMUTE(0, 20);
            PERMUTE(1, 4);
            PERMUTE(2, 1);
			break;
		case 2:
            PERMUTE(0, 5);
            PERMUTE(1, 1);
			break;
		case 1:
            PERMUTE(0, 1);
			break;
	}
#undef PERMUTE
    
	/* Recompute the actual register values based on the position-relative values. */
	bool position_used[PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX+1] = { 0 };
    
	for (uint32_t i = 0; i < count; ++i) {
		int renumbered = 0;
		for (int u = 1; u < 7; u++) {
			if (!position_used[u]) {
				if (renumbered == permunreg[i]) {
					registers[i] = u;
					position_used[u] = true;
					break;
				}
				renumbered++;
			}
		}
	}
    
    return PLCRASH_ESUCCESS;
}