在Java卡中将整数的二进制表示形式转换为ASCII
作者:互联网
我想将以二进制格式表示的整数的任意长度转换为ASCII形式.
一个示例是整数33023,十六进制字节为0x80ff.我想将0x80ff表示为33023的ASCII格式,其十六进制表示形式为0x3333303233.
我正在无法识别String类型的Java Card环境中工作,因此我将不得不通过二进制操作手动进行转换.
解决此问题的最有效方法是什么,因为16位智能卡上的Java Card环境非常受限.
解决方法:
这比您想象的要棘手,因为它需要基数转换,并且基数转换使用大整数算法在整数上执行.
当然,这并不意味着我们不能为此目的专门创建上述大整数算法的有效实现.这是一种将零填充(通常在Java卡上是必需的)而不使用额外内存(!)的实现.但是,如果要保留大端数字,则可能必须复制其原始值-输入值将被覆盖.强烈建议将其放入RAM.
这段代码简单地将字节除以新的基数(十进制为10),返回余数.其余为下一个最低位数.由于输入值现在已经被除,下一个余数就是仅比前一个有效位高一位的数字.它将继续除法并返回余数,直到该值为零并且计算完成.
该算法的棘手部分是内部循环,该循环将值除以10后就位,同时使用尾数除以字节来返回余数.每次运行可提供一个余数/十进制数字.这也意味着函数的顺序为O(n),其中n是结果中的位数(将尾部除法定义为单个操作).请注意,n可以通过ceil(bigNumBytes * log_10(256))计算:其结果也存在于预先计算的BCD_SIZE_PER_BYTES表中. log_10(256)当然是一个恒定的十进制值,高于2.408.
这是经过优化的最终代码(请参见不同版本的编辑内容):
/**
* Converts an unsigned big endian value within the buffer to the same value
* stored using ASCII digits. The ASCII digits may be zero padded, depending
* on the value within the buffer.
* <p>
* <strong>Warning:</strong> this method zeros the value in the buffer that
* contains the original number. It is strongly recommended that the input
* value is in fast transient memory as it will be overwritten multiple
* times - until it is all zero.
* </p>
* <p>
* <strong>Warning:</strong> this method fails if not enough bytes are
* available in the output BCD buffer while destroying the input buffer.
* </p>
* <p>
* <strong>Warning:</strong> the big endian number can only occupy 16 bytes
* or less for this implementation.
* </p>
*
* @param uBigBuf
* the buffer containing the unsigned big endian number
* @param uBigOff
* the offset of the unsigned big endian number in the buffer
* @param uBigLen
* the length of the unsigned big endian number in the buffer
* @param decBuf
* the buffer that is to receive the BCD encoded number
* @param decOff
* the offset in the buffer to receive the BCD encoded number
* @return decLen, the length in the buffer of the received BCD encoded
* number
*/
public static short toDecimalASCII(byte[] uBigBuf, short uBigOff,
short uBigLen, byte[] decBuf, short decOff) {
// variables required to perform long division by 10 over bytes
// possible optimization: reuse remainder for dividend (yuk!)
short dividend, division, remainder;
// calculate stuff outside of loop
final short uBigEnd = (short) (uBigOff + uBigLen);
final short decDigits = BYTES_TO_DECIMAL_SIZE[uBigLen];
// --- basically perform division by 10 in a loop, storing the remainder
// traverse from right (least significant) to the left for the decimals
for (short decIndex = (short) (decOff + decDigits - 1); decIndex >= decOff; decIndex--) {
// --- the following code performs tail division by 10 over bytes
// clear remainder at the start of the division
remainder = 0;
// traverse from left (most significant) to the right for the input
for (short uBigIndex = uBigOff; uBigIndex < uBigEnd; uBigIndex++) {
// get rest of previous result times 256 (bytes are base 256)
// ... and add next positive byte value
// optimization: doing shift by 8 positions instead of mul.
dividend = (short) ((remainder << 8) + (uBigBuf[uBigIndex] & 0xFF));
// do the division
division = (short) (dividend / 10);
// optimization: perform the modular calculation using
// ... subtraction and multiplication
// ... instead of calculating the remainder directly
remainder = (short) (dividend - division * 10);
// store the result in place for the next iteration
uBigBuf[uBigIndex] = (byte) division;
}
// the remainder is what we were after
// add '0' value to create ASCII digits
decBuf[decIndex] = (byte) (remainder + '0');
}
return decDigits;
}
/*
* pre-calculated array storing the number of decimal digits for big endian
* encoded number with len bytes: ceil(len * log_10(256))
*/
private static final byte[] BYTES_TO_DECIMAL_SIZE = { 0, 3, 5, 8, 10, 13,
15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39 };
要扩展输入大小,只需计算下一个十进制大小并将其存储在表中…
标签:binary,javacard,java,integer 来源: https://codeday.me/bug/20191108/2008828.html