如何重写C后藤和切换与惯用的Rust等价物在bzip 2的函数?

iq0todco  于 2024-01-08  发布在  其他
关注(0)|答案(1)|浏览(139)

我正在从C语言过渡到Rust,并且正在寻找一种惯用的方法来重写一个C代码模式,该模式涉及Rust中的switch case和后藤。下面是我的C代码示例:
C代码是从bzip2中提取的 *.i代码

#include <stdio.h>
int main() {
    int state = 15;
    int dummy = 0;
    switch (state) {
        case 15:
            dummy = 15; // set dummy to 15 under certain condition
            if (dummy == 15) goto endhdr_2;
            /* fallthrough */

        case 16:
            dummy = 16;
            printf("dummy");
            /* fallthrough */

        endhdr_2:
        case 42:
            dummy = 42;
            printf("dummy: %d", 42);
            /* fallthrough */

        case 43:
            dummy = 43;
            printf("dummy: %d", 43);
            /* fallthrough */
        
        default:
            break;
    }
    return 0;
}

字符串
我知道Rust中没有goto,而Rust的match与C的switch有些不同。我如何才能最好地将这种模式转换到Rust中,同时遵循Rust的惯用做法并确保相同的功能?

为上述代码添加额外的Context

上面我的C代码的例子,它是从classic C program bzip2的代码库中提取的,goto逻辑具体来自decompress. c中的BZ2_decompress函数。
我在这里附加了.i预处理的可读性,它来自.i文件。
decompress.i中使用后藤的位置

case 14: s->state = 14; while (((Bool)1)) { if (s->bsLive >= 8) { UInt32 v; v = (s->bsBuff >> (s->bsLive-8)) & ((1 << 8)-1); s->bsLive -= 8; uc = v; break; } if (s->strm->avail_in == 0) { retVal = 0; goto save_state_and_return; };; s->bsBuff = (s->bsBuff << 8) | ((UInt32) (*((UChar*)(s->strm->next_in)))); s->bsLive += 8; s->strm->next_in++; s->strm->avail_in--; s->strm->total_in_lo32++; if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; };

if (uc == 0x17) goto endhdr_2;


decompress.iendhdr_2:之后的代码

endhdr_2:

      case 42: s->state = 42; while (((Bool)1)) { if (s->bsLive >= 8) { UInt32 v; v = (s->bsBuff >> (s->bsLive-8)) & ((1 << 8)-1); s->bsLive -= 8; uc = v; break; } if (s->strm->avail_in == 0) { retVal = 0; goto save_state_and_return; };; s->bsBuff = (s->bsBuff << 8) | ((UInt32) (*((UChar*)(s->strm->next_in)))); s->bsLive += 8; s->strm->next_in++; s->strm->avail_in--; s->strm->total_in_lo32++; if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; };
      if (uc != 0x72) { retVal = (-4); goto save_state_and_return; };;
      case 43: s->state = 43; while (((Bool)1)) { if (s->bsLive >= 8) { UInt32 v; v = (s->bsBuff >> (s->bsLive-8)) & ((1 << 8)-1); s->bsLive -= 8; uc = v; break; } if (s->strm->avail_in == 0) { retVal = 0; goto save_state_and_return; };; s->bsBuff = (s->bsBuff << 8) | ((UInt32) (*((UChar*)(s->strm->next_in)))); s->bsLive += 8; s->strm->next_in++; s->strm->avail_in--; s->strm->total_in_lo32++; if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; };
      if (uc != 0x45) { retVal = (-4); goto save_state_and_return; };;
      case 44: s->state = 44; while (((Bool)1)) { if (s->bsLive >= 8) { UInt32 v; v = (s->bsBuff >> (s->bsLive-8)) & ((1 << 8)-1); s->bsLive -= 8; uc = v; break; } if (s->strm->avail_in == 0) { retVal = 0; goto save_state_and_return; };; s->bsBuff = (s->bsBuff << 8) | ((UInt32) (*((UChar*)(s->strm->next_in)))); s->bsLive += 8; s->strm->next_in++; s->strm->avail_in--; s->strm->total_in_lo32++; if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; };
      if (uc != 0x38) { retVal = (-4); goto save_state_and_return; };;
      case 45: s->state = 45; while (((Bool)1)) { if (s->bsLive >= 8) { UInt32 v; v = (s->bsBuff >> (s->bsLive-8)) & ((1 << 8)-1); s->bsLive -= 8; uc = v; break; } if (s->strm->avail_in == 0) { retVal = 0; goto save_state_and_return; };; s->bsBuff = (s->bsBuff << 8) | ((UInt32) (*((UChar*)(s->strm->next_in)))); s->bsLive += 8; s->strm->next_in++; s->strm->avail_in--; s->strm->total_in_lo32++; if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; };
      if (uc != 0x50) { retVal = (-4); goto save_state_and_return; };;
      case 46: s->state = 46; while (((Bool)1)) { if (s->bsLive >= 8) { UInt32 v; v = (s->bsBuff >> (s->bsLive-8)) & ((1 << 8)-1); s->bsLive -= 8; uc = v; break; } if (s->strm->avail_in == 0) { retVal = 0; goto save_state_and_return; };; s->bsBuff = (s->bsBuff << 8) | ((UInt32) (*((UChar*)(s->strm->next_in)))); s->bsLive += 8; s->strm->next_in++; s->strm->avail_in--; s->strm->total_in_lo32++; if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; };
      if (uc != 0x90) { retVal = (-4); goto save_state_and_return; };;


您可以通过修改CMakeLists.txt来添加add_definitions(-save-temps)来重现decompress.i
您可以使用以下命令清理 *.i

for file in *.i; do awk '!/^#[ \t]*[0-9]+[ \t]+"/' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"; done

brqmpdu1

brqmpdu11#

为了消除每一个goto和从一种情况到下一种情况的每一次下降,我们可以重新构造代码,

Int32 retVal = 0;
...
switch ( s->state ) {
   ...

   case ...:
      ...
      if ( ... )
         goto endhdr_2;  // Go to state 42.
      ...

   ...

   endhdr_2:
   case 42:
      s->state = 42;
      ...
      if ( ... ) {
         retVal = ...;
         // Exit the switch while skipping the assert.
         goto save_state_and_return;
      }
      ...
      // Go to state 43.

   case 43:
      s->state = 43;
      ...

   ...
}

...

save_state_and_return:
...

字符串
成为

Int32 retVal = 1;  // An illegal value for which we'll check for later.
...
while ( 1 ) {
   switch ( s->state ) {
      ...

      case ...:
         ...
         if ( ... ) {
            // Go to state 42.
            s->state = 42;
            continue;
         }
         ...

      ...

      case 42:
         ...
         if ( ... ) {
            retVal = ...;
            break;
         }

         ...

         // Go to state 43.
         s->state = 43;
         continue;

      case 43:
         ...

      ...
   }

   break;
}

if ( retVal == 1 ) {
   ...
   retVal = 0;
}

...


从我刚刚读到的关于Rust的内容来看,这应该很容易翻译。
接下来是我们如何做到这一点。

消除goto endhdr_2

由于在goto之后要做的第一件事是更改switch变量,因此我们可以重新构造代码,

switch ( s->state ) {
   ...

   case ...:
      ...
      if ( ... )
         goto endhdr_2;  // Go to state 42.
      ...

   ...

   endhdr_2:
   case 42:
      s->state = 42;
      ...

   ...
}


成为

while ( 1 ) {
   switch ( s->state ) {
      ...

      case ...:
         ...
         if ( ... ) {
            // Go to state 42.
            s->state = 42;
            continue;
         }
         ...

      ...

      case 42:
         s->state = 42;
         ...

      ...
   }

   break;
}

消除跌倒

据我所知,Rust不支持从一种情况下降到下一种情况,而C代码确实做了很多。这种用于消除goto的方法可以用来模拟下降到另一种状态。

switch ( s->state ) {
   ...

   case 42:
      ...

      // Go to state 43.

   case 43:
      s->state = 43;
      ...

   ...
}


成为

while ( 1 ) {
   switch ( s->state ) {
      ...

      case 42:
         ...

         // Go to state 43.
         s->state = 43;
         continue;

      case 43:
         ...

      ...
   }

   break;
}

消除goto save_state_and_return

第二个goto有“百万”个示例:goto save_state_and_returngoto的目的是退出开关。goto代替break用于跳过开关之后的Assert。因此,只要我们还设置一些变量来指示应该跳过Assert,就可以将goto替换为break。我们可以采用我们总是在每个goto save_state_and_return之前设置retVal,以避免设置新变量“百万”次。换句话说,我们可以重新构造代码,

Int32 retVal = 0;
...
switch ( s->state ) {
   ...

   case ...:
      ...
      if ( ... ) {
         retVal = ...;
         // Exit the switch while skipping the assert.
         goto save_state_and_return;
      }
      ...

   ...
}

...

save_state_and_return:
...


成为

Int32 retVal = 1;  // An illegal value for which we'll check for later.
...
switch ( s->state ) {
   ...

   case ...:
      ...
      if ( ... ) {
         retVal = ...;
         break;
      }
      ...

   ...
}

if ( retVal == 1 ) {
   ...
   retVal = 0;
}

...

相关问题