Bonjour à tous,
Je suis en train d'essayer de comprendre un bout de code
issu de Contiki pour débugguer un firmware assez spécifique et je
tombe sur des choses bizarres avec des switches comme :
{
switch(a);
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; // <- ??????
} while(0);
}
...
}
Naturellement, ces bouts de code sont masqués dans des macros (que
j'ai remplacées par leurs définitions car les macros ne font pas ce
qu'elles sont censées faire dans la doc...).
Ce code compile sur le cross-compilateur fourni par le fabricant
pour sa plate-forme. Pourtant, le 'case c:' ne me semble pas licite,
la construction 'switch' étant à cheval sur au moins une boucle
issue de la macro elle-même ! Un avis autorisé ?
Bonjour à tous,
Je suis en train d'essayer de comprendre un bout de code
issu de Contiki pour débugguer un firmware assez spécifique et je
tombe sur des choses bizarres avec des switches comme :
{
switch(a);
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; // <- ??????
} while(0);
}
...
}
Naturellement, ces bouts de code sont masqués dans des macros (que
j'ai remplacées par leurs définitions car les macros ne font pas ce
qu'elles sont censées faire dans la doc...).
Ce code compile sur le cross-compilateur fourni par le fabricant
pour sa plate-forme. Pourtant, le 'case c:' ne me semble pas licite,
la construction 'switch' étant à cheval sur au moins une boucle
issue de la macro elle-même ! Un avis autorisé ?
Bonjour à tous,
Je suis en train d'essayer de comprendre un bout de code
issu de Contiki pour débugguer un firmware assez spécifique et je
tombe sur des choses bizarres avec des switches comme :
{
switch(a);
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; // <- ??????
} while(0);
}
...
}
Naturellement, ces bouts de code sont masqués dans des macros (que
j'ai remplacées par leurs définitions car les macros ne font pas ce
qu'elles sont censées faire dans la doc...).
Ce code compile sur le cross-compilateur fourni par le fabricant
pour sa plate-forme. Pourtant, le 'case c:' ne me semble pas licite,
la construction 'switch' étant à cheval sur au moins une boucle
issue de la macro elle-même ! Un avis autorisé ?
switch(a);
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; //<- ??????
} while(0);
}
...
}
switch(a);
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; //<- ??????
} while(0);
}
...
}
switch(a);
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; //<- ??????
} while(0);
}
...
}
In article ,
JKB wrote:Bonjour à tous,
Je suis en train d'essayer de comprendre un bout de code
issu de Contiki pour débugguer un firmware assez spécifique et je
tombe sur des choses bizarres avec des switches comme :
{
switch(a);
ben deja, le ; la me semble suspect.
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; // <- ??????
} while(0);
}
...
}
Naturellement, ces bouts de code sont masqués dans des macros (que
j'ai remplacées par leurs définitions car les macros ne font pas ce
qu'elles sont censées faire dans la doc...).
Ce code compile sur le cross-compilateur fourni par le fabricant
pour sa plate-forme. Pourtant, le 'case c:' ne me semble pas licite,
la construction 'switch' étant à cheval sur au moins une boucle
issue de la macro elle-même ! Un avis autorisé ?
Tu voudrais pas nous montrer un fragment COMPLET reellement issu du
preprocesseur, meme si c'est tres moche ?
Je pense que ce que tu as la, ca doit etre une variante de Duff's device
(confere wikipedia ou assimilie) qui est a la fois un des hacks les plus
crades et les plus "beaux" du langage, mais vu le ; en trop, je soupconne
que tu t'es aussi trompe sur les imbrications d'accolades dans le code
genere, et que c'est plutot un truc du genre:
switch(e) {
case a:
...
case b:
...
for (;;) {
do {
case c: ;
} while (0);
}
}
ce qui, meme si assez bizarre, est licite (meme si ca ressemble beaucoup
a un goto en plein milieu du code, et donc qu'il se peut que certaines
initialisations manquent).
In article <slrnk1fqba.n7c.jkb@rayleigh.systella.fr>,
JKB <jkb@koenigsberg.invalid> wrote:
Bonjour à tous,
Je suis en train d'essayer de comprendre un bout de code
issu de Contiki pour débugguer un firmware assez spécifique et je
tombe sur des choses bizarres avec des switches comme :
{
switch(a);
ben deja, le ; la me semble suspect.
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; // <- ??????
} while(0);
}
...
}
Naturellement, ces bouts de code sont masqués dans des macros (que
j'ai remplacées par leurs définitions car les macros ne font pas ce
qu'elles sont censées faire dans la doc...).
Ce code compile sur le cross-compilateur fourni par le fabricant
pour sa plate-forme. Pourtant, le 'case c:' ne me semble pas licite,
la construction 'switch' étant à cheval sur au moins une boucle
issue de la macro elle-même ! Un avis autorisé ?
Tu voudrais pas nous montrer un fragment COMPLET reellement issu du
preprocesseur, meme si c'est tres moche ?
Je pense que ce que tu as la, ca doit etre une variante de Duff's device
(confere wikipedia ou assimilie) qui est a la fois un des hacks les plus
crades et les plus "beaux" du langage, mais vu le ; en trop, je soupconne
que tu t'es aussi trompe sur les imbrications d'accolades dans le code
genere, et que c'est plutot un truc du genre:
switch(e) {
case a:
...
case b:
...
for (;;) {
do {
case c: ;
} while (0);
}
}
ce qui, meme si assez bizarre, est licite (meme si ca ressemble beaucoup
a un goto en plein milieu du code, et donc qu'il se peut que certaines
initialisations manquent).
In article ,
JKB wrote:Bonjour à tous,
Je suis en train d'essayer de comprendre un bout de code
issu de Contiki pour débugguer un firmware assez spécifique et je
tombe sur des choses bizarres avec des switches comme :
{
switch(a);
ben deja, le ; la me semble suspect.
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; // <- ??????
} while(0);
}
...
}
Naturellement, ces bouts de code sont masqués dans des macros (que
j'ai remplacées par leurs définitions car les macros ne font pas ce
qu'elles sont censées faire dans la doc...).
Ce code compile sur le cross-compilateur fourni par le fabricant
pour sa plate-forme. Pourtant, le 'case c:' ne me semble pas licite,
la construction 'switch' étant à cheval sur au moins une boucle
issue de la macro elle-même ! Un avis autorisé ?
Tu voudrais pas nous montrer un fragment COMPLET reellement issu du
preprocesseur, meme si c'est tres moche ?
Je pense que ce que tu as la, ca doit etre une variante de Duff's device
(confere wikipedia ou assimilie) qui est a la fois un des hacks les plus
crades et les plus "beaux" du langage, mais vu le ; en trop, je soupconne
que tu t'es aussi trompe sur les imbrications d'accolades dans le code
genere, et que c'est plutot un truc du genre:
switch(e) {
case a:
...
case b:
...
for (;;) {
do {
case c: ;
} while (0);
}
}
ce qui, meme si assez bizarre, est licite (meme si ca ressemble beaucoup
a un goto en plein milieu du code, et donc qu'il se peut que certaines
initialisations manquent).
Le 31/07/2012 16:19, JKB a écrit :switch(a);
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; //<- ??????
} while(0);
}
...
}
J'ai vu passer un jour des trucs dans le même genre avec un switch
et des case au milieu de blocks pour pouvoir faire un code qui
fonctionne dans l'esprit des continuations (je ne suis plus très sur
de ce mot). En gros toutes les variables C sont dans une structure
appelée contexte, et cette dernière contient un champ "état" qui sert au
switch pour reprendre l'algo à son dernier point d’exécution.
Cela permet de tronçonner l’exécution d'un algorithme autrement
monolithique.
Voyez ci bas la présence de case en plein milieu de if(), avec des
accolades apriori mal appariées.
----8<---------------------------------------------------------------
int
exo_read_decrunched_byte(struct exo_decrunch_ctx *ctx)
{
int c;
int length_index;
struct exo_table_entry *table_entry;
switch(ctx->state)
{
case STATE_NEXT_BYTE:
if(read_bits(ctx, 1) == 1)
{
/* literal byte */
c = ctx->read_byte(ctx->read_data);
break;
}
/* sequence */
length_index = get_gamma_code(ctx);
if(length_index == 17)
{
/* literal data block */
ctx->length = read_bits(ctx, 16);
ctx->state = STATE_NEXT_LITERAL_BYTE;
case STATE_NEXT_LITERAL_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = ctx->read_byte(ctx->read_data);
break;
}
else if(length_index == 16)
{
/* end of data marker, we're done. */
ctx->state = STATE_EOF;
case STATE_EOF:
return -1;
}
/* sequence */
table_entry = ctx->lengths + length_index;
ctx->length = table_entry->base + read_bits(ctx,
table_entry->bits);
switch(ctx->length)
{
case 1:
table_entry = ctx->offsets1 + read_bits(ctx, 2);
break;
case 2:
table_entry = ctx->offsets2 + read_bits(ctx, 4);
break;
default:
table_entry = ctx->offsets3 + read_bits(ctx, 4);
}
ctx->offset = table_entry->base + read_bits(ctx,
table_entry->bits);
ctx->state = STATE_NEXT_SEQUENCE_BYTE;
case STATE_NEXT_SEQUENCE_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = read_byte_from_window(ctx, ctx->offset);
break;
}
ctx->window[ctx->window_pos++] = (unsigned char)c;
if(ctx->window_pos == ctx->window_length)
{
ctx->window_pos = 0;
}
return c;
}
----8<---------------------------------------------------------------
Ce code est vraiment tordu, il y a des case en plein milieu de if(),
des fall-though à la fin des cases,... aie aie aie.. et pourtant il
se compile très bien. De ce que j'en ai pensé c'est que le case n'est
finalement jamais plus qu'une sorte d'étiquette... mais une étiquette
sans goto car le goto-c'est-mal(tm). Le saut est simplement calculé lors
du switch(). C'est un vrai truc de tricheur en somme cette histoire ;-)
Le 31/07/2012 16:19, JKB a écrit :
switch(a);
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; //<- ??????
} while(0);
}
...
}
J'ai vu passer un jour des trucs dans le même genre avec un switch
et des case au milieu de blocks pour pouvoir faire un code qui
fonctionne dans l'esprit des continuations (je ne suis plus très sur
de ce mot). En gros toutes les variables C sont dans une structure
appelée contexte, et cette dernière contient un champ "état" qui sert au
switch pour reprendre l'algo à son dernier point d’exécution.
Cela permet de tronçonner l’exécution d'un algorithme autrement
monolithique.
Voyez ci bas la présence de case en plein milieu de if(), avec des
accolades apriori mal appariées.
----8<---------------------------------------------------------------
int
exo_read_decrunched_byte(struct exo_decrunch_ctx *ctx)
{
int c;
int length_index;
struct exo_table_entry *table_entry;
switch(ctx->state)
{
case STATE_NEXT_BYTE:
if(read_bits(ctx, 1) == 1)
{
/* literal byte */
c = ctx->read_byte(ctx->read_data);
break;
}
/* sequence */
length_index = get_gamma_code(ctx);
if(length_index == 17)
{
/* literal data block */
ctx->length = read_bits(ctx, 16);
ctx->state = STATE_NEXT_LITERAL_BYTE;
case STATE_NEXT_LITERAL_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = ctx->read_byte(ctx->read_data);
break;
}
else if(length_index == 16)
{
/* end of data marker, we're done. */
ctx->state = STATE_EOF;
case STATE_EOF:
return -1;
}
/* sequence */
table_entry = ctx->lengths + length_index;
ctx->length = table_entry->base + read_bits(ctx,
table_entry->bits);
switch(ctx->length)
{
case 1:
table_entry = ctx->offsets1 + read_bits(ctx, 2);
break;
case 2:
table_entry = ctx->offsets2 + read_bits(ctx, 4);
break;
default:
table_entry = ctx->offsets3 + read_bits(ctx, 4);
}
ctx->offset = table_entry->base + read_bits(ctx,
table_entry->bits);
ctx->state = STATE_NEXT_SEQUENCE_BYTE;
case STATE_NEXT_SEQUENCE_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = read_byte_from_window(ctx, ctx->offset);
break;
}
ctx->window[ctx->window_pos++] = (unsigned char)c;
if(ctx->window_pos == ctx->window_length)
{
ctx->window_pos = 0;
}
return c;
}
----8<---------------------------------------------------------------
Ce code est vraiment tordu, il y a des case en plein milieu de if(),
des fall-though à la fin des cases,... aie aie aie.. et pourtant il
se compile très bien. De ce que j'en ai pensé c'est que le case n'est
finalement jamais plus qu'une sorte d'étiquette... mais une étiquette
sans goto car le goto-c'est-mal(tm). Le saut est simplement calculé lors
du switch(). C'est un vrai truc de tricheur en somme cette histoire ;-)
Le 31/07/2012 16:19, JKB a écrit :switch(a);
...
{
case a:
...
case b:
...
}
for(...;...;...)
{
do
{
...
case c:; //<- ??????
} while(0);
}
...
}
J'ai vu passer un jour des trucs dans le même genre avec un switch
et des case au milieu de blocks pour pouvoir faire un code qui
fonctionne dans l'esprit des continuations (je ne suis plus très sur
de ce mot). En gros toutes les variables C sont dans une structure
appelée contexte, et cette dernière contient un champ "état" qui sert au
switch pour reprendre l'algo à son dernier point d’exécution.
Cela permet de tronçonner l’exécution d'un algorithme autrement
monolithique.
Voyez ci bas la présence de case en plein milieu de if(), avec des
accolades apriori mal appariées.
----8<---------------------------------------------------------------
int
exo_read_decrunched_byte(struct exo_decrunch_ctx *ctx)
{
int c;
int length_index;
struct exo_table_entry *table_entry;
switch(ctx->state)
{
case STATE_NEXT_BYTE:
if(read_bits(ctx, 1) == 1)
{
/* literal byte */
c = ctx->read_byte(ctx->read_data);
break;
}
/* sequence */
length_index = get_gamma_code(ctx);
if(length_index == 17)
{
/* literal data block */
ctx->length = read_bits(ctx, 16);
ctx->state = STATE_NEXT_LITERAL_BYTE;
case STATE_NEXT_LITERAL_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = ctx->read_byte(ctx->read_data);
break;
}
else if(length_index == 16)
{
/* end of data marker, we're done. */
ctx->state = STATE_EOF;
case STATE_EOF:
return -1;
}
/* sequence */
table_entry = ctx->lengths + length_index;
ctx->length = table_entry->base + read_bits(ctx,
table_entry->bits);
switch(ctx->length)
{
case 1:
table_entry = ctx->offsets1 + read_bits(ctx, 2);
break;
case 2:
table_entry = ctx->offsets2 + read_bits(ctx, 4);
break;
default:
table_entry = ctx->offsets3 + read_bits(ctx, 4);
}
ctx->offset = table_entry->base + read_bits(ctx,
table_entry->bits);
ctx->state = STATE_NEXT_SEQUENCE_BYTE;
case STATE_NEXT_SEQUENCE_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = read_byte_from_window(ctx, ctx->offset);
break;
}
ctx->window[ctx->window_pos++] = (unsigned char)c;
if(ctx->window_pos == ctx->window_length)
{
ctx->window_pos = 0;
}
return c;
}
----8<---------------------------------------------------------------
Ce code est vraiment tordu, il y a des case en plein milieu de if(),
des fall-though à la fin des cases,... aie aie aie.. et pourtant il
se compile très bien. De ce que j'en ai pensé c'est que le case n'est
finalement jamais plus qu'une sorte d'étiquette... mais une étiquette
sans goto car le goto-c'est-mal(tm). Le saut est simplement calculé lors
du switch(). C'est un vrai truc de tricheur en somme cette histoire ;-)
Je vais essayer de copier une sortie du préprocesseur. Mais il est
vrai qu'à bien y réfléchir l'accolade après le case b: semble en
trop. Ce qui me chiffonnait, c'est qu'on puisse sauter directement
au case c: en sautant l'initialisation de for() et de do {. Ça doit
faire un vrai bazar sur la pile...
Je vais essayer de copier une sortie du préprocesseur. Mais il est
vrai qu'à bien y réfléchir l'accolade après le case b: semble en
trop. Ce qui me chiffonnait, c'est qu'on puisse sauter directement
au case c: en sautant l'initialisation de for() et de do {. Ça doit
faire un vrai bazar sur la pile...
Je vais essayer de copier une sortie du préprocesseur. Mais il est
vrai qu'à bien y réfléchir l'accolade après le case b: semble en
trop. Ce qui me chiffonnait, c'est qu'on puisse sauter directement
au case c: en sautant l'initialisation de for() et de do {. Ça doit
faire un vrai bazar sur la pile...
int
exo_read_decrunched_byte(struct exo_decrunch_ctx *ctx)
{
int c;
int length_index;
struct exo_table_entry *table_entry;
switch(ctx->state)
{
case STATE_NEXT_BYTE:
if(read_bits(ctx, 1) == 1)
{
/* literal byte */
c = ctx->read_byte(ctx->read_data);
break;
}
/* sequence */
length_index = get_gamma_code(ctx);
if(length_index == 17)
{
/* literal data block */
ctx->length = read_bits(ctx, 16);
ctx->state = STATE_NEXT_LITERAL_BYTE;
case STATE_NEXT_LITERAL_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = ctx->read_byte(ctx->read_data);
break;
}
else if(length_index == 16)
{
/* end of data marker, we're done. */
ctx->state = STATE_EOF;
case STATE_EOF:
return -1;
}
/* sequence */
table_entry = ctx->lengths + length_index;
ctx->length = table_entry->base + read_bits(ctx,
table_entry->bits);
switch(ctx->length)
{
case 1:
table_entry = ctx->offsets1 + read_bits(ctx, 2);
break;
case 2:
table_entry = ctx->offsets2 + read_bits(ctx, 4);
break;
default:
table_entry = ctx->offsets3 + read_bits(ctx, 4);
}
ctx->offset = table_entry->base + read_bits(ctx,
table_entry->bits);
ctx->state = STATE_NEXT_SEQUENCE_BYTE;
case STATE_NEXT_SEQUENCE_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = read_byte_from_window(ctx, ctx->offset);
break;
}
ctx->window[ctx->window_pos++] = (unsigned char)c;
if(ctx->window_pos == ctx->window_length)
{
ctx->window_pos = 0;
}
return c;
}
int
exo_read_decrunched_byte(struct exo_decrunch_ctx *ctx)
{
int c;
int length_index;
struct exo_table_entry *table_entry;
switch(ctx->state)
{
case STATE_NEXT_BYTE:
if(read_bits(ctx, 1) == 1)
{
/* literal byte */
c = ctx->read_byte(ctx->read_data);
break;
}
/* sequence */
length_index = get_gamma_code(ctx);
if(length_index == 17)
{
/* literal data block */
ctx->length = read_bits(ctx, 16);
ctx->state = STATE_NEXT_LITERAL_BYTE;
case STATE_NEXT_LITERAL_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = ctx->read_byte(ctx->read_data);
break;
}
else if(length_index == 16)
{
/* end of data marker, we're done. */
ctx->state = STATE_EOF;
case STATE_EOF:
return -1;
}
/* sequence */
table_entry = ctx->lengths + length_index;
ctx->length = table_entry->base + read_bits(ctx,
table_entry->bits);
switch(ctx->length)
{
case 1:
table_entry = ctx->offsets1 + read_bits(ctx, 2);
break;
case 2:
table_entry = ctx->offsets2 + read_bits(ctx, 4);
break;
default:
table_entry = ctx->offsets3 + read_bits(ctx, 4);
}
ctx->offset = table_entry->base + read_bits(ctx,
table_entry->bits);
ctx->state = STATE_NEXT_SEQUENCE_BYTE;
case STATE_NEXT_SEQUENCE_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = read_byte_from_window(ctx, ctx->offset);
break;
}
ctx->window[ctx->window_pos++] = (unsigned char)c;
if(ctx->window_pos == ctx->window_length)
{
ctx->window_pos = 0;
}
return c;
}
int
exo_read_decrunched_byte(struct exo_decrunch_ctx *ctx)
{
int c;
int length_index;
struct exo_table_entry *table_entry;
switch(ctx->state)
{
case STATE_NEXT_BYTE:
if(read_bits(ctx, 1) == 1)
{
/* literal byte */
c = ctx->read_byte(ctx->read_data);
break;
}
/* sequence */
length_index = get_gamma_code(ctx);
if(length_index == 17)
{
/* literal data block */
ctx->length = read_bits(ctx, 16);
ctx->state = STATE_NEXT_LITERAL_BYTE;
case STATE_NEXT_LITERAL_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = ctx->read_byte(ctx->read_data);
break;
}
else if(length_index == 16)
{
/* end of data marker, we're done. */
ctx->state = STATE_EOF;
case STATE_EOF:
return -1;
}
/* sequence */
table_entry = ctx->lengths + length_index;
ctx->length = table_entry->base + read_bits(ctx,
table_entry->bits);
switch(ctx->length)
{
case 1:
table_entry = ctx->offsets1 + read_bits(ctx, 2);
break;
case 2:
table_entry = ctx->offsets2 + read_bits(ctx, 4);
break;
default:
table_entry = ctx->offsets3 + read_bits(ctx, 4);
}
ctx->offset = table_entry->base + read_bits(ctx,
table_entry->bits);
ctx->state = STATE_NEXT_SEQUENCE_BYTE;
case STATE_NEXT_SEQUENCE_BYTE:
if(--ctx->length == 0)
{
ctx->state = STATE_NEXT_BYTE;
}
c = read_byte_from_window(ctx, ctx->offset);
break;
}
ctx->window[ctx->window_pos++] = (unsigned char)c;
if(ctx->window_pos == ctx->window_length)
{
ctx->window_pos = 0;
}
return c;
}
Ce qui me chiffonnait, c'est qu'on puisse sauter directement
au case c: en sautant l'initialisation de for() et de do {. Ça doit
faire un vrai bazar sur la pile...
Ce qui me chiffonnait, c'est qu'on puisse sauter directement
au case c: en sautant l'initialisation de for() et de do {. Ça doit
faire un vrai bazar sur la pile...
Ce qui me chiffonnait, c'est qu'on puisse sauter directement
au case c: en sautant l'initialisation de for() et de do {. Ça doit
faire un vrai bazar sur la pile...