将此 C 函数转换为 PHP


Translate this C function to 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();

有人可以指出我解决这个问题的正确方向吗?

我认为

问题是($index << c) 3,586,129,920>20亿,并且不能用有符号的32位整数正确表示。由于您没有在 php 中显式定义$value的数据类型,因此我认为算术最终取决于实现。

实际上,令人惊讶的是,事情根本有效 - 我认为,您将 32 位数字移动大于 32 的值将导致未定义的行为。您可能需要重新考虑基础数学,特别是考虑代码的下溢/溢出行为。

作为一个潜在的解决方案,您可能会注意到您有有限数量的可能输入和相应的输出 - 您实际上可以创建一个直接查找表。我相信我这样做是正确的(使用经过一些修改的代码的C++版本) - 它没有导致 1:1 映射让我有点惊讶。查找字符串变为:

$lookupString = "6RQtrpp07TU4AP1IDKmjl8QD7WjitmwUAcjT3AT9MuAu3PUKJtIb5vS"

您的 php 代码可以简化为

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

其中55lookupString的长度。

有趣的观察:许多字符出现不止一次;其他字符从不使用。这意味着这不是一个非常"好"的编码方案(如果这是它想要的)。

作为参考,这是我用来确定查找字符串的代码:

#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;
}

你几乎肯定会遇到"问题",因为你在32位PHP上运行,或者在Windows上运行PHP(无论操作系统位数如何,它都不支持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在今年晚些时候发布时,这个问题应该会得到解决。