[PATCH] binutils: Use loops in `byte_get_*_endian` functions instead of switch cases.

Jan Beulich jbeulich@suse.com
Fri Jul 10 08:32:24 GMT 2026


On 06.07.2026 18:59, Julien Thillard wrote:
> Switch cases are used to read a value with a given size, but
> these functions could be rewritten using a small loop instead,
> avoiding code repetition.
> This is already done in `byte_put_*_endian` functions.
> 
> binutils:
> 
>         * elfcomm.c: Use loop instead of switch case.

While the change looks technically correct (apart from ...

> --- a/binutils/elfcomm.c
> +++ b/binutils/elfcomm.c
> @@ -114,127 +114,35 @@ uint64_t (*byte_get) (const unsigned char *, unsigned int);
>  uint64_t
>  byte_get_little_endian (const unsigned char *field, unsigned int size)
>  {
> -  switch (size)
> -    {
> -    case 1:
> -      return *field;
> -
> -    case 2:
> -      return ((uint64_t) field[0]
> -	      | ((uint64_t) field[1] << 8));
> -
> -    case 3:
> -      return ((uint64_t) field[0]
> -	      | ((uint64_t) field[1] << 8)
> -	      | ((uint64_t) field[2] << 16));
> -
> -    case 4:
> -      return ((uint64_t) field[0]
> -	      | ((uint64_t) field[1] << 8)
> -	      | ((uint64_t) field[2] << 16)
> -	      | ((uint64_t) field[3] << 24));
> -
> -    case 5:
> -      return ((uint64_t) field[0]
> -	      | ((uint64_t) field[1] << 8)
> -	      | ((uint64_t) field[2] << 16)
> -	      | ((uint64_t) field[3] << 24)
> -	      | ((uint64_t) field[4] << 32));
> +  unsigned int i;
> +  uint64_t read = 0;
>  
> -    case 6:
> -      return ((uint64_t) field[0]
> -	      | ((uint64_t) field[1] << 8)
> -	      | ((uint64_t) field[2] << 16)
> -	      | ((uint64_t) field[3] << 24)
> -	      | ((uint64_t) field[4] << 32)
> -	      | ((uint64_t) field[5] << 40));
> -
> -    case 7:
> -      return ((uint64_t) field[0]
> -	      | ((uint64_t) field[1] << 8)
> -	      | ((uint64_t) field[2] << 16)
> -	      | ((uint64_t) field[3] << 24)
> -	      | ((uint64_t) field[4] << 32)
> -	      | ((uint64_t) field[5] << 40)
> -	      | ((uint64_t) field[6] << 48));
> -
> -    case 8:
> -      return ((uint64_t) field[0]
> -	      | ((uint64_t) field[1] << 8)
> -	      | ((uint64_t) field[2] << 16)
> -	      | ((uint64_t) field[3] << 24)
> -	      | ((uint64_t) field[4] << 32)
> -	      | ((uint64_t) field[5] << 40)
> -	      | ((uint64_t) field[6] << 48)
> -	      | ((uint64_t) field[7] << 56));
> -
> -    default:
> +  if (size > sizeof (uint64_t))
> +    {
>        error (_("Unhandled data length: %d\n"), size);
>        abort ();
>      }
> +  for(i = 0; i < size; i++)

... a style issue here [missing blank after "for"] and again in the other
loop), how does generated code change? There may have been a reason things
were coded this way.

There's also a subtle, possibly (but not necessarily) benign change in
behavior: size being 0 previously hit the default: label, while now we'd
silently return 0 in that case (which may be correct in some cases, but
which may not be desired in others).

Maybe this as a compromise, reducing code size while retaining original
properties?

uint64_t
byte_get_little_endian (const unsigned char *field, unsigned int size)
{
  uint64_t read = 0;

  switch (size)
    {
    case 8:
      read |= (uint64_t) field[7] << 56;
      /* Fall through.  */
    case 7:
      read |= (uint64_t) field[6] << 48;
      /* Fall through.  */
    case 6:
      read |= (uint64_t) field[5] << 40;
      /* Fall through.  */
    case 5:
      read |= (uint64_t) field[4] << 32;
      /* Fall through.  */
    case 4:
      read |= (uint64_t) field[3] << 24;
      /* Fall through.  */
    case 3:
      read |= (uint64_t) field[2] << 16;
      /* Fall through.  */
    case 2:
      read |= (uint64_t) field[1] <<  8;
      /* Fall through.  */
    case 1:
      return read | *field;

    default:
      error (_("Unhandled data length: %u\n"), size);
      abort ();
    }
}

Jan


More information about the Binutils mailing list