Перевести эту функцию C на PHP

Я пытаюсь перевести следующий код C, который в основном просто пытается преобразовать произвольное целочисленное значение в символ из пула символов, в PHP:

#include <cstdint>
#include <cstring>
#include <iostream>

uint8_t GetCharacter(uint32_t value) {
static const char* valid_characters = "0123456789ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw";
static const size_t valid_characters_l = strlen(valid_characters);
uint8_t c = valid_characters[value % valid_characters_l];
return valid_characters[(value << c) % valid_characters_l];
}

int main() {
uint32_t array[] = {176, 52, 608, 855};
for (size_t i=0; i < 4; i++) {
uint8_t c = GetCharacter(array[i]);
std::cout << array[i] << ": " << (uint32_t) c << "\n";
}
return 0;
}

Который дает

176: 109
52: 114
608: 85
855: 65

Однако PHP-код, который я смог придумать, дает следующее:

176: 109
52: 114
608: 85
855: 104   // << Here's the problem

Я очень уверен, что перевел это точно, и я не могу найти проблему.

<?php

function getCharacter($index) {
$chars = "0123456789ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw";
$c = ord(substr($chars, $index % strlen($chars)));
return ord(substr($chars, ($index << $c) % strlen($chars)));
}

function main() {
$array = array(176, 52, 608, 855);
foreach ($array as $value) {
echo "$value: " . getCharacter($value) . "\n";
}
}

main();

Может ли кто-нибудь указать мне правильное направление для решения этой проблемы?

0

Решение

Я считаю, что проблема в том, что число ($index << c) является 3,586,129,920 что> 2 миллиарда, и не может быть должным образом представлено 32-разрядным целым числом со знаком. Поскольку вы явно не определяете тип данных $value в php я думаю, что арифметика зависит от реализации.

На самом деле удивительно, что все работает вообще — вы сдвигаете 32-битное число на значение больше 32, что, я думаю, приведет к неопределенному поведению. Возможно, вы захотите переосмыслить основную математику и, в частности, учесть поведение вашего кода при переполнении / переполнении.

В качестве потенциального решения вы можете заметить, что у вас есть конечное число возможных входов и соответствующих выходов — вы могли бы фактически создать таблицу прямого просмотра. Я считаю, что я сделал это правильно (используя версию вашего кода на C ++ с некоторыми изменениями) — меня немного удивило, что это не привело к отображению 1: 1. Строка поиска становится:

$lookupString = "6RQtrpp07TU4AP1IDKmjl8QD7WjitmwUAcjT3AT9MuAu3PUKJtIb5vS"

И ваш PHP-код может быть уменьшен до

$value = ord(substr($lookupString, $input % 55));

куда 55 это длина lookupString,

Интересное наблюдение: ряд символов появляется более одного раза; другие символы никогда не используются. Это означает, что это не очень «хорошая» схема кодирования (если это то, чем она пытается быть).

Для справки, это код, который я использовал для определения строки поиска:

#include <cstring>
#include <iostream>

static const char* valid_characters = "0123456789ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw";

uint8_t GetCharacter(uint32_t value) {
static const size_t valid_characters_l = strlen(valid_characters);
uint8_t c = valid_characters[value % valid_characters_l];
return valid_characters[(value << c) % valid_characters_l];
}

int main() {
uint32_t array[] = {176, 52, 608, 855};
for (size_t i=0; i < 55; i++) {
uint8_t c = GetCharacter(i + '0');
std::cout << char(c);
}
std::cout << "\n";
return 0;
}
2

Другие решения

Вы почти наверняка столкнетесь с «проблемой», потому что вы работаете на 32-битном PHP или PHP на Windows (который не поддерживает 64-битные целые числа независимо от разрядности ОС). Проблема в том, что вы переполняете целое число в операции сдвига:

64-битный PHP:

PHP_INT_MAX: 9223372036854775807
C: 66, index: 176, strlen: 55, shift: 704, substr: mnopqrstuvw :: 176: 109
C: 117, index: 52, strlen: 55, shift: 468374361246531584, substr: 9ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw :: 52: 57
C: 51, index: 608, strlen: 55, shift: 1369094286720630784, substr: hijklmnopqrstuvw :: 608: 104
C: 86, index: 855, strlen: 55, shift: 3586129920, substr: ABCDEFGHIJKLMOPQRSTUVWabcdefghijklmnopqrstuvw :: 855: 65

32-битный PHP:

PHP_INT_MAX: 2147483647
C: 66, index: 176, strlen: 55, shift: 704, substr: mnopqrstuvw :: 176: 109
C: 117, index: 52, strlen: 55, shift: 109051904, substr: rstuvw :: 52: 114
C: 51, index: 608, strlen: 55, shift: 318767104, substr: UVWabcdefghijklmnopqrstuvw :: 608: 85
C: 86, index: 855, strlen: 55, shift: -708837376, substr: hijklmnopqrstuvw :: 855: 104

К сожалению, PHP вообще не поддерживает длинные целые числа в 32-битных системах (пока). Единственный способ обойти это было бы через внешний пакет, такой как GMP или же BCMath. Когда PHP v7.0 будет выпущен в конце этого года, эта проблема должно быть исправлено.

1