Преобразование двоичного представления целых чисел в ASCII в Java Card - PullRequest
0 голосов
/ 16 сентября 2018

Я хотел бы преобразовать произвольную длину целых чисел, представленных в двоичном формате, в форму ASCII.

Один из примеров - целое число 33023, шестнадцатеричные байты - 0x80ff.Я хотел бы представить 0x80ff в формате ASCII 33023, который имеет шестнадцатеричное представление 0x3333303233.

Я работаю в среде Java Card , которая не распознаетСтроковый тип, поэтому мне придется выполнять преобразование вручную с помощью бинарных манипуляций.

Какой наиболее эффективный способ решить эту проблему, поскольку среда Java-карты на 16-битной смарт-карте является очень ограниченным.

1 Ответ

0 голосов
/ 17 сентября 2018

Это сложнее, чем вы думаете, так как требуется базовое преобразование, и базовое преобразование выполняется для всего числа с использованием большой целочисленной арифметики.

Это, конечно, не означает, что мы не можем создать эффективную реализацию упомянутой большой целочисленной арифметики специально для этой цели. Вот реализация, которая оставляет пэды с нулями (что обычно требуется на Java Card) и не использует дополнительную память (!). Возможно, вам придется скопировать оригинальное значение с прямым порядковым номером, если вы хотите сохранить его - входное значение перезаписывается. Настоятельно рекомендуется поместить его в оперативную память.

Этот код просто делит байты с новым основанием (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 };

Чтобы расширить входной размер, просто рассчитайте и сохраните следующие десятичные размеры в таблице ...

...