致命错误:没有足够的位来表示传递的值

Posted

技术标签:

【中文标题】致命错误:没有足够的位来表示传递的值【英文标题】:Fatal error: Not enough bits to represent the passed value 【发布时间】:2019-07-19 02:01:22 【问题描述】:

尝试使用用 Swift 编写的 Mikrotik API 库: https://wiki.mikrotik.com/wiki/API_in_Swift

当我发送小命令时效果很好

但是,如果我尝试发送大的脚本字符串,我会收到错误:

崩溃的代码:

private func writeLen(_ command : String) -> Data 
    let data = command.data(using: String.Encoding.utf8)
    var len = data?.count ?? 0
    var dat = Data()

    if len < 0x80 
        dat.append([UInt8(len)], count: 1)
    else if len < 0x4000 
        len = len | 0x8000;
        dat.append(Data(bytes: [UInt8(len >> 8)]))
        dat.append(Data(bytes: [UInt8(len)]))
    else if len < 0x20000 
        len = len | 0xC00000;
        dat.append(Data(bytes: [UInt8(len >> 16)]))
        dat.append(Data(bytes: [UInt8(len >> 8)]))
        dat.append(Data(bytes: [UInt8(len)]))
    
    else if len < 0x10000000 
        len = len | 0xE0000000;
        dat.append(Data(bytes: [UInt8(len >> 24)]))
        dat.append(Data(bytes: [UInt8(len >> 16)]))
        dat.append(Data(bytes: [UInt8(len >> 8)]))
        dat.append(Data(bytes: [UInt8(len)]))
    else
        dat.append(Data(bytes: [0xF0]))
        dat.append(Data(bytes: [UInt8(len >> 24)]))
        dat.append(Data(bytes: [UInt8(len >> 16)]))
        dat.append(Data(bytes: [UInt8(len >> 8)]))
        dat.append(Data(bytes: [UInt8(len)]))
    

    return dat

致命错误出现在这部分:

else if len < 0x4000 
    len = len | 0x8000;
    dat.append(Data(bytes: [UInt8(len >> 8)]))
    dat.append(Data(bytes: [UInt8(len)]))

在线:

dat.append(Data(bytes: [UInt8(len)]))

此时数据大小为1072字节,len等于33840,UInt8不能用那个len值初始化。

如何编辑代码以避免错误?

我正在使用 Swift 4.2

编辑:

这是一个相同逻辑但用 javascript 编写的示例

module.exports.encodeString = function encodeString(s) 
var data = null;
var len = Buffer.byteLength(s);
var offset = 0;
if (len < 0x80) 
    data = new Buffer(len + 1);
    data[offset++] = len;
 else if (len < 0x4000) 
    data = new Buffer(len + 2);
    len |= 0x8000;
    data[offset++] = (len >> 8) & 0xff;
    data[offset++] = len & 0xff;
 else if (len < 0x200000) 
    data = new Buffer(len + 3);
    len |= 0xC00000;
    data[offset++] = (len >> 16) & 0xff;
    data[offset++] = (len >> 8) & 0xff;
    data[offset++] = len & 0xff;
 else if (len < 0x10000000) 
    data = new Buffer(len + 4);
    len |= 0xE0000000;
    data[offset++] = (len >> 24) & 0xff;
    data[offset++] = (len >> 16) & 0xff;
    data[offset++] = (len >> 8) & 0xff;
    data[offset++] = len & 0xff;
 else 
    data = new Buffer(len + 5);
    data[offset++] = 0xF0;
    data[offset++] = (len >> 24) & 0xff;
    data[offset++] = (len >> 16) & 0xff;
    data[offset++] = (len >> 8) & 0xff;
    data[offset++] = len & 0xff;

data.utf8Write(s, offset);
return data;
;

也许有人看到了区别

【问题讨论】:

@matt,正如我所说,我正在使用来自 MikroTik wiki 的代码示例。你能推荐一个可以正常工作的代码吗? @matt,好吧,它肯定停止了崩溃,但套接字也停止了对该命令的响应。小命令虽然有效。 好的,既然您已经在 J​​avaScript 中展示了工作代码,那么解决方案就很简单了。 【参考方案1】:

感谢 JavaScript 翻译。它清楚地显示了问题,因为 Swift 版本与它不相似。

让我们来看看这段 JavaScript,因为它是你在 Swift 中遇到的部分:

 else if (len < 0x4000) 
    data = new Buffer(len + 2);
    len |= 0x8000;
    data[offset++] = (len >> 8) & 0xff;
    data[offset++] = len & 0xff;

在 Swift 中是这样“翻译”的:

 else if len < 0x4000 
    len = len | 0x8000;
    dat.append(Data(bytes: [UInt8(len >> 8)]))
    dat.append(Data(bytes: [UInt8(len)]))
 

嗯,您可以立即看到它们完全不同。在最后一行,Swift 版本忘记了&amp; 0xff

如果你把它放进去,一切都会开始工作。我们也可以让它看起来更像 JavaScript 原版:

 else if len < 0x4000 
    len |= 0x8000;
    dat.append(Data(bytes: [UInt8(len >> 8)]))
    dat.append(Data(bytes: [UInt8(len & 0xff)]))

所以我会说,是的,使用 JavaScript 作为指南,你会没事的。如果最后一行对你来说不够“迅速”,那么就这样写吧:

    dat.append(Data(bytes: [UInt8(truncatingIfNeeded: len)]))

结果完全一样。

我不保证在您进行这些更改后一切都会完美运行(在我看来,您展示的 Swift 代码仍然不像 JavaScript 那样做同样的事情),但至少在我们编写的部分数据开头的长度字节将正常工作。

【讨论】:

没问题,是你通过发布 JavaScript 来解决的。

以上是关于致命错误:没有足够的位来表示传递的值的主要内容,如果未能解决你的问题,请参考以下文章

SQL 错误:ORA-00947:没有足够的值 - 尝试使用默认值

致命错误:未捕获的类型错误:参数 1 传递给 PhpMyAdmin\UserPreferences::apply()

Symfony-Catchable致命错误:传递给Doctrine Common Collections ArrayCollection :: __ construct()的参数1必须是类(代码

Oracle存储过程抛出“没有足够的值”错误

OPEN_CV 错误没有足够的值来解包(预期 3,得到 2)[重复]

PHPUnit 显示抛出致命错误异常的传递方法