duansanzi5265 2015-08-19 13:17
浏览 37
已采纳

可变长度二进制补码int64

I'm attempting to write a Go program to parse ans.1 BER two's complement integer encoding. However, the integer can have either 1, 2, 3 or 4 byte length encoding (depending on its size).

According to the specification (http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf) the leftmost bit is always the complement.

What's a clean way to do this?

func ParseInt(b []byte) (int64, error) {
    switch len(b) {
    case 1:
        // this works
        return int64(b[0]&0x7f) - int64(b[0]&0x80), nil
    case 2:
        // left most byte of b[0] is -32768
    case 3:
        // left most byte of b[0] is -8388608
    case 4:
        // left most byte of b[0] is -2147483648 (and so on for 5, 6, 7, 8)
    case 5:
    case 6:
    case 7:
    case 8:
    default:
        return 0, errors.New("value does not fit in a int64")
    }
}


ParseInt([]byte{0xfe})       // should return (-2, nil)
ParseInt([]byte{0xfe, 0xff}) // should return (-257, nil)
ParseInt([]byte{0x01, 0x00}) // should return (256, nil)
  • 写回答

1条回答 默认 最新

  • dongye3917 2015-08-19 14:29
    关注

    Easier to understand if you read the bytes from the end:

    • You don't have to shift the last byte
    • Left-shift the last byte by 8 (8 bits in a byte)
    • Left-shift the 2nd last byte by 16
    • ...
    • And from the first byte only use 7 bits, the leftmost bit is special.

    The leftmost bit of the first byte b[0]&080 tells if you have to add an offset to the result. The offset to be optionally added is -1 multiplied by the number your input would mean by having this one bit set and all others being 0, that is -1 * (1 << (len(b)*8 - 1)) = 0x80 << (len(b)*8 - 8).

    Examples. If input is...

    • 1 byte:
      int64(b[0]&0x7f) - int64(b[0]&0x80)
    • 2 bytes:
      int64(b[0]&0x7f)<<8 + int64(b[1]) - int64(b[0]&0x80)<<8
    • 3 bytes:
      int64(b[0]&0x7f)<<16 + int64(b[1])<<8 + int64(b[2]) - int64(b[0]&0x80)<<16

    All these cases can be covered with a nice loop.

    Here's a compact implementation (try it on the <kbd>Go Playground</kbd>):

    func ParseInt(b []byte) (int64, error) {
        if len(b) > 8 {
            return 0, errors.New("value does not fit in a int64")
        }
    
        var n int64
        for i, v := range b {
            shift := uint((len(b) - i - 1) * 8)
            if i == 0 && v&0x80 != 0 {
                n -= 0x80 << shift
                v &= 0x7f
            }
            n += int64(v) << shift
        }
        return n, nil
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 关于模型训练的一个问题
  • ¥15 装了几千台服务器从来没遇到这种问题,哎看下哪位帮我解决吧
  • ¥15 单片机程序上的困难问题
  • ¥15 请教某软件缓存Ts文件破解合并mp4的方法
  • ¥15 求小游戏炸弹人中关于敌人的C++代码
  • ¥15 拿到这个服务器最高权限有偿
  • ¥50 来个抓app跳转支付宝转链接的
  • ¥15 remotes安装提示没有description文件
  • ¥15 AttributeError: 'NoneType' object has no attribute 'drop_duplicates'报错
  • ¥15 以下代码,运行结果报错