Go转JS实现prepareMessage函数时出现偏移长度越界RangeError报错
Go字节处理函数转JS实现越界错误修复
问题背景
尝试将Go语言实现的prepareMessage字节组帧函数转写为JavaScript,代码运行时抛出缓冲区越界错误,已知问题和缓冲区操作逻辑相关,需要具体修复方案。
原始参考Go代码
func prepareMessage(data []byte) []byte { // Compute CRC before modifying the message. crc := crcCompute(data) // Add the two CRC16 bytes before replacing control characters. data = append(data, byte(crc&0xFF)) data = append(data, byte(crc>>8)) tmp := []byte{0x7E} // Flag start. // Copy the message over, escaping 0x7E (Flag Byte) and 0x7D (Control-Escape). for i := 0; i < len(data); i++ { mv := data[i] if (mv == 0x7E) || (mv == 0x7D) { mv = mv ^ 0x20 tmp = append(tmp, 0x7D) } tmp = append(tmp, mv) } tmp = append(tmp, 0x7E) // Flag end. return tmp }
存在问题的初始JS实现
var _appendBuffer = function (buffer1, buffer2) { var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength); tmp.set(new Uint8Array(buffer1), 0); tmp.set(new Uint8Array(buffer2), buffer1.byteLength); return tmp.buffer; }; function prepareMessage(data) { // Compute CRC before modifying the message. var crc = crcCompute(data) // Add the two CRC16 bytes before replacing control characters. data = data + (crc & 0xFF); data = data + (crc >> 8); var tmp = new ArrayBuffer(1000000000); tmp[0] = [0x7E]; // Flag start. // Copy the message over, escaping 0x7E (Flag Byte) and 0x7D (Control-Escape). for (var i = 0; i < data.length; i++) { var mv = data[i] if ((mv == 0x7E) || (mv == 0x7D)) { mv = mv ^ 0x20; tmp = _appendBuffer(tmp, [0x7D]); } tmp = _appendBuffer(tmp, mv); } tmp = _appendBuffer(tmp, [0x7E]); // Flag end. return tmp; }
报错信息
执行tmp = _appendBuffer(tmp, mv);时抛出错误:RangeError: Range consisting of offset and length are out of bounds
错误根因
- 字节拼接逻辑完全错误:使用
+运算符拼接ArrayBuffer和数字时,JS会自动将操作数转为字符串,完全无法实现Go中append追加字节的效果,后续按下标读取值会得到NaN ArrayBuffer操作方式错误:ArrayBuffer是原始二进制缓冲区,不能直接通过下标赋值/取值,必须通过Uint8Array等类型视图操作;初始创建1GB大小的缓冲区属于无意义的内存浪费- 工具函数传参类型错误:
_appendBuffer要求第二个参数是带byteLength属性的二进制对象,但实际传入的mv是单个数字,buffer2.byteLength为undefined,计算新缓冲区长度时得到NaN,最终触发越界 - 转义逻辑的字节处理没有做类型对齐,容易出现非8位无符号整数的异常值
修复后可运行代码
注意:需提前实现入参为Uint8Array、返回16位CRC整数值的crcCompute函数
function prepareMessage(inputData) { // 统一将输入转为Uint8Array,对齐Go的[]byte类型 const data = new Uint8Array(inputData); const crc = crcCompute(data); // 拼接原数据与CRC校验值,低字节在前、高字节在后,和Go逻辑完全一致 const dataWithCrc = new Uint8Array(data.length + 2); dataWithCrc.set(data, 0); dataWithCrc[data.length] = crc & 0xFF; dataWithCrc[data.length + 1] = (crc >> 8) & 0xFF; // 用普通数组暂存字节,避免手动计算缓冲区长度的繁琐逻辑 const frameBytes = [0x7E]; for (let i = 0; i < dataWithCrc.length; i++) { let byteVal = dataWithCrc[i]; if (byteVal === 0x7E || byteVal === 0x7D) { byteVal ^= 0x20; frameBytes.push(0x7D); } frameBytes.push(byteVal); } frameBytes.push(0x7E); // 最终转为Uint8Array返回,和Go字节切片行为对齐 return new Uint8Array(frameBytes); }
关键修改说明
- 移除了有逻辑缺陷的
_appendBuffer函数,改用JS数组动态追加字节,最后统一转Uint8Array,从根源上避免缓冲区长度计算错误 - 修正了CRC值的拼接逻辑,不再使用
+做非法类型转换,通过Uint8Array.set方法正确写入两个CRC字节,字节序和原Go代码完全匹配 - 删除了初始创建1GB超大缓冲区的无效逻辑,按需生成数组,内存占用合理
- 所有字节值都做了8位无符号整数对齐,不会出现类型转换导致的异常值
内容的提问来源于stack exchange,提问作者Leon
相关产品推荐
相关产品推荐

