用JS实现CRC8校验
Implement CRC8 checksum with JS
已经尝试过 CRC8,但无法获得正确的校验和。
有谁知道如何使用 JS 生成此校验和?
JavaScript中的校验和计算
在默认情况下数字是双精度浮点数的语言中进行整数按位数学运算可能非常困难。您的代码必须确保数字保持为整数,甚至可能保持无符号且在特定位范围(16 位、32 位)内。这些额外的步骤可能会使事情复杂化。
技巧
确保一个数字是 x 位整数的一些技巧是使用 AND 运算符和一个允许该范围的所有位的位掩码。例如确保 16 位数字可以通过数字 &= 0xffff; 完成。此外,我使用诸如 num | 之类的操作0 或 num >>> 0 以确保它是 32 位整数,有符号或无符号。这是防止产生负面结果所必需的,当您以十六进制显示校验和时,这看起来特别奇怪。
速度
显然我并没有尝试这样做来制作一个快速校验和引擎,我只是想在我的 SmallPRNG 和 ISAAC CPRNG 笔(post 待定 ;))之后测试更多按位计算的可能性。这些算法在所有浏览器和浏览器版本中的表现都不同,甚至在某些浏览器中可能会非常慢。我相信 Chrome 会非常快速地处理数据,并且可以以合理的速度计算校验和!
基地class
我将实施多个校验和算法,因此我将创建一个基础 class,它将为所选算法构建一个状态 (ctx)。此状态将跟踪校验和和算法实例 class。此 class 还将正确处理字符串,并考虑到它们的编码。
此 class 还包含对 Uint8Array 支持的测试。我不确定这是否是测试支持的最佳方式,但它确实起到了作用。
var hasTyped = (function() {
if(!('Uint8Array' in window)) {
return false;
}
try {
var a = new window.Uint8Array(10);
a[0] = 100;
if(a[0] === 100) {
return true;
}
return false;
} catch (e) {
return false;
}
}());
算法classes
现在我们可以添加带有 Checksum.registerChecksum 的算法。每个算法 class 应该实现一个单独的和一个数组方法来处理数据,还可以选择实现一个设置方法,该方法将在构造 Checksum 对象时调用。
BSD16
这是一个非常简单的算法,这个算法只需要一点代码。 BSD 校验和算法!
(function() {
'use strict';
if(typeof(window.Checksum) === "undefined") {
throw "The Checksum class is required for this code.";
}
/**
* Initialize anything you want in the constructor, such as setting up a lookup
* table of bytes. Make sure to cache your calculations, so they only have to be
* done once.
*/
var BSD16 = function BSD16() {
this.init = 0;
};
/**
* bsd16 for arrays, each value must be numeric and will be bound to 8-bits (Int8Array or Uint8Array works best!)
* @param {Array} a input (8-bit array)
* @param {Number} p previous checksum state
* @returns {Number} new checksum state
*/
BSD16.prototype.array = function(a, p) {
var c = p || 0, i = 0, l = a.length;
for(; i < l; i++) c = (((((c >>> 1) + ((c & 1) << 15)) | 0) + (a[i] & 0xff)) & 0xffff) | 0;
return c;
};
/**
* bsd16 for a single value, update a checksum with a new byte
* @param {Number} b byte (0-255)
* @param {Number} p previous checksum state
* @returns {Number} new checksum state
*/
BSD16.prototype.single = function(b, p) {
var c = p || 0;
return (((((c >>> 1) + ((c & 1) << 15)) | 0) + (b & 0xff)) & 0xffff) | 0;
};
Checksum.registerChecksum("bsd16", BSD16);
}());
FNV32(FNV-0 和 FNV-1)
另一个简单的算法,FNV 哈希算法 - 生成 32 位校验和!
(function() {
'use strict';
if(typeof(window.Checksum) === "undefined") {
throw "The Checksum class is required for this code.";
}
var prime = 0x01000193;
/**
* Initialize anything you want in the constructor, such as setting up a lookup
* table of bytes. Make sure to cache your calculations, so they only have to be
* done once.
*/
var FNV32 = function FNV32() {
this.init = 2166136261; // fnv-1!
};
/**
* The setup method which will be called when new Checksum("fletcher", ...) is called.
* This method is supposed to initialize the checksum cipher and to recieve parameters
* from the constructor.
*
* @param {Number} mode the FNV32 mode (FNV-1 (defualt) or FNV-0)
*/
FNV32.prototype.setup = function(mode) {
if(mode === 0) {
this.init = 0; // fnv-0.
}
};
FNV32.prototype.array = function(a, p) {
var len = a.length,
fnv = p || this.init;
for(var i = 0; i < len; i++) {
fnv = (fnv + (((fnv << 1) + (fnv << 4) + (fnv << 7) + (fnv << 8) + (fnv << 24)) >>> 0)) ^ (a[i] & 0xff);
}
return fnv >>> 0;
};
FNV32.prototype.single = function(b, p) {
var fnv = p || this.init;
return ((fnv + (((fnv << 1) + (fnv << 4) + (fnv << 7) + (fnv << 8) + (fnv << 24)) >>> 0)) ^ (b & 0xff)) >>> 0;
};
Checksum.registerChecksum("fnv32", FNV32);
}());
您可以使用此 url 阅读全文
这被证明是一个非常棘手的问题,但我想我有一个解决方案。它是 Java 的 Java 脚本端口(大部分)。我对代码做了一些简化,以消除似乎不会影响答案的东西。
首先,我必须在您的 Java 程序中导出 CRC8_DATA
的十六进制等价物。我只使用一个简单的 bytesToHex
例程实现了这一点,我发现 here (这部分在 Java 中):
System.out.print(bytesToHex(CRC8_DATA));
...
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
一旦我有了这个 table,我就把 Java 代码转换成 Java 脚本,得到这个:
var CRC8_DATA = '005EBCE2613FDD83C29C7E20A3FD1F419DC3217FFCA2401E5F01E3BD3E6082DC237D9FC1421CFEA0E1BF5D0380DE3C62BEE0025CDF81633D7C22C09E1D43A1FF4618FAA427799BC584DA3866E5BB5907DB856739BAE406581947A5FB7826C49A653BD987045AB8E6A7F91B45C6987A24F8A6441A99C7257B3A6486D85B05E7B98CD2306EEDB3510F4E10F2AC2F7193CD114FADF3702ECC92D38D6F31B2EC0E50AFF1134DCE90722C6D33D18F0C52B0EE326C8ED0530DEFB1F0AE4C1291CF2D73CA947628ABF517490856B4EA6937D58B5709EBB536688AD495CB2977F4AA4816E9B7550B88D6346A2B7597C94A14F6A8742AC896154BA9F7B6E80A54D7896B35';
function strToArr(str) {
var arr = str.match(/[0-9a-f]{2}/ig); // convert into array of hex pairs
arr = arr.map(x=> parseInt(x, 16)); // convert hex pairs into ints (bytes)
return arr;
}
CRC8_DATA = strToArr(CRC8_DATA);
function calculateCRC8(bArr) {
var i = 1;
var i2 = bArr.length - 1;
var b = 0;
while (i <= i2) {
b = CRC8_DATA[(b ^ bArr[i]) & 255];
i++;
}
return b;
}
function calcFromString(str) {
// convert from string to "byte" array
var byte_array = strToArr(str);
var checksum = calculateCRC8(byte_array)
console.log(str, checksum.toString(16));
}
calcFromString('02FD 1000 2322 4978 0140 00AF 6000 0000 0000');
calcFromString('02FD 1000 D82E 4F76 0189 00AF FA14 0000 0000');
原来的Java是以i=1
开头的,所以在校验和计算中实际上不包括第一个字节。这可能是许多 JavaScript 库没有给出相同答案的原因之一。
我回去把这个和网上的这个进行了比较CRC calculator。消除第一个字节 (0x02) 时,我能够使用 CRC-8/MAXIM;DOW-CRC
获得等效结果。
我无法让原来的 fiddle 正常工作,即使在删除第一个字节并更改多项式以匹配该网站的多项式之后也是如此。其他一些选项也必须不同。
已经尝试过 CRC8,但无法获得正确的校验和。
有谁知道如何使用 JS 生成此校验和?
JavaScript中的校验和计算 在默认情况下数字是双精度浮点数的语言中进行整数按位数学运算可能非常困难。您的代码必须确保数字保持为整数,甚至可能保持无符号且在特定位范围(16 位、32 位)内。这些额外的步骤可能会使事情复杂化。
技巧 确保一个数字是 x 位整数的一些技巧是使用 AND 运算符和一个允许该范围的所有位的位掩码。例如确保 16 位数字可以通过数字 &= 0xffff; 完成。此外,我使用诸如 num | 之类的操作0 或 num >>> 0 以确保它是 32 位整数,有符号或无符号。这是防止产生负面结果所必需的,当您以十六进制显示校验和时,这看起来特别奇怪。
速度 显然我并没有尝试这样做来制作一个快速校验和引擎,我只是想在我的 SmallPRNG 和 ISAAC CPRNG 笔(post 待定 ;))之后测试更多按位计算的可能性。这些算法在所有浏览器和浏览器版本中的表现都不同,甚至在某些浏览器中可能会非常慢。我相信 Chrome 会非常快速地处理数据,并且可以以合理的速度计算校验和!
基地class 我将实施多个校验和算法,因此我将创建一个基础 class,它将为所选算法构建一个状态 (ctx)。此状态将跟踪校验和和算法实例 class。此 class 还将正确处理字符串,并考虑到它们的编码。
此 class 还包含对 Uint8Array 支持的测试。我不确定这是否是测试支持的最佳方式,但它确实起到了作用。
var hasTyped = (function() {
if(!('Uint8Array' in window)) {
return false;
}
try {
var a = new window.Uint8Array(10);
a[0] = 100;
if(a[0] === 100) {
return true;
}
return false;
} catch (e) {
return false;
}
}());
算法classes 现在我们可以添加带有 Checksum.registerChecksum 的算法。每个算法 class 应该实现一个单独的和一个数组方法来处理数据,还可以选择实现一个设置方法,该方法将在构造 Checksum 对象时调用。
BSD16 这是一个非常简单的算法,这个算法只需要一点代码。 BSD 校验和算法!
(function() {
'use strict';
if(typeof(window.Checksum) === "undefined") {
throw "The Checksum class is required for this code.";
}
/**
* Initialize anything you want in the constructor, such as setting up a lookup
* table of bytes. Make sure to cache your calculations, so they only have to be
* done once.
*/
var BSD16 = function BSD16() {
this.init = 0;
};
/**
* bsd16 for arrays, each value must be numeric and will be bound to 8-bits (Int8Array or Uint8Array works best!)
* @param {Array} a input (8-bit array)
* @param {Number} p previous checksum state
* @returns {Number} new checksum state
*/
BSD16.prototype.array = function(a, p) {
var c = p || 0, i = 0, l = a.length;
for(; i < l; i++) c = (((((c >>> 1) + ((c & 1) << 15)) | 0) + (a[i] & 0xff)) & 0xffff) | 0;
return c;
};
/**
* bsd16 for a single value, update a checksum with a new byte
* @param {Number} b byte (0-255)
* @param {Number} p previous checksum state
* @returns {Number} new checksum state
*/
BSD16.prototype.single = function(b, p) {
var c = p || 0;
return (((((c >>> 1) + ((c & 1) << 15)) | 0) + (b & 0xff)) & 0xffff) | 0;
};
Checksum.registerChecksum("bsd16", BSD16);
}());
FNV32(FNV-0 和 FNV-1)
另一个简单的算法,FNV 哈希算法 - 生成 32 位校验和!
(function() {
'use strict';
if(typeof(window.Checksum) === "undefined") {
throw "The Checksum class is required for this code.";
}
var prime = 0x01000193;
/**
* Initialize anything you want in the constructor, such as setting up a lookup
* table of bytes. Make sure to cache your calculations, so they only have to be
* done once.
*/
var FNV32 = function FNV32() {
this.init = 2166136261; // fnv-1!
};
/**
* The setup method which will be called when new Checksum("fletcher", ...) is called.
* This method is supposed to initialize the checksum cipher and to recieve parameters
* from the constructor.
*
* @param {Number} mode the FNV32 mode (FNV-1 (defualt) or FNV-0)
*/
FNV32.prototype.setup = function(mode) {
if(mode === 0) {
this.init = 0; // fnv-0.
}
};
FNV32.prototype.array = function(a, p) {
var len = a.length,
fnv = p || this.init;
for(var i = 0; i < len; i++) {
fnv = (fnv + (((fnv << 1) + (fnv << 4) + (fnv << 7) + (fnv << 8) + (fnv << 24)) >>> 0)) ^ (a[i] & 0xff);
}
return fnv >>> 0;
};
FNV32.prototype.single = function(b, p) {
var fnv = p || this.init;
return ((fnv + (((fnv << 1) + (fnv << 4) + (fnv << 7) + (fnv << 8) + (fnv << 24)) >>> 0)) ^ (b & 0xff)) >>> 0;
};
Checksum.registerChecksum("fnv32", FNV32);
}());
您可以使用此 url 阅读全文
这被证明是一个非常棘手的问题,但我想我有一个解决方案。它是 Java 的 Java 脚本端口(大部分)。我对代码做了一些简化,以消除似乎不会影响答案的东西。
首先,我必须在您的 Java 程序中导出 CRC8_DATA
的十六进制等价物。我只使用一个简单的 bytesToHex
例程实现了这一点,我发现 here (这部分在 Java 中):
System.out.print(bytesToHex(CRC8_DATA));
...
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
一旦我有了这个 table,我就把 Java 代码转换成 Java 脚本,得到这个:
var CRC8_DATA = '005EBCE2613FDD83C29C7E20A3FD1F419DC3217FFCA2401E5F01E3BD3E6082DC237D9FC1421CFEA0E1BF5D0380DE3C62BEE0025CDF81633D7C22C09E1D43A1FF4618FAA427799BC584DA3866E5BB5907DB856739BAE406581947A5FB7826C49A653BD987045AB8E6A7F91B45C6987A24F8A6441A99C7257B3A6486D85B05E7B98CD2306EEDB3510F4E10F2AC2F7193CD114FADF3702ECC92D38D6F31B2EC0E50AFF1134DCE90722C6D33D18F0C52B0EE326C8ED0530DEFB1F0AE4C1291CF2D73CA947628ABF517490856B4EA6937D58B5709EBB536688AD495CB2977F4AA4816E9B7550B88D6346A2B7597C94A14F6A8742AC896154BA9F7B6E80A54D7896B35';
function strToArr(str) {
var arr = str.match(/[0-9a-f]{2}/ig); // convert into array of hex pairs
arr = arr.map(x=> parseInt(x, 16)); // convert hex pairs into ints (bytes)
return arr;
}
CRC8_DATA = strToArr(CRC8_DATA);
function calculateCRC8(bArr) {
var i = 1;
var i2 = bArr.length - 1;
var b = 0;
while (i <= i2) {
b = CRC8_DATA[(b ^ bArr[i]) & 255];
i++;
}
return b;
}
function calcFromString(str) {
// convert from string to "byte" array
var byte_array = strToArr(str);
var checksum = calculateCRC8(byte_array)
console.log(str, checksum.toString(16));
}
calcFromString('02FD 1000 2322 4978 0140 00AF 6000 0000 0000');
calcFromString('02FD 1000 D82E 4F76 0189 00AF FA14 0000 0000');
原来的Java是以i=1
开头的,所以在校验和计算中实际上不包括第一个字节。这可能是许多 JavaScript 库没有给出相同答案的原因之一。
我回去把这个和网上的这个进行了比较CRC calculator。消除第一个字节 (0x02) 时,我能够使用 CRC-8/MAXIM;DOW-CRC
获得等效结果。
我无法让原来的 fiddle 正常工作,即使在删除第一个字节并更改多项式以匹配该网站的多项式之后也是如此。其他一些选项也必须不同。