-
Notifications
You must be signed in to change notification settings - Fork 20
Description
I noticed that while single integers (readInt()) are correctly read as signed values, readIntArray() uses raw unpacking (V*) without sign conversion. This causes negative numbers in arrays to be deserialized as large unsigned integers on 64-bit PHP.
This behavior seems inconsistent with the rest of the library, where IntTag correctly preserves the sign.
Test Code:
$serializer = new LittleEndianNbtSerializer();
// Test values including edges of 32-bit signed integers
$values = [
1337,
-127,
Limits::INT32_MAX, // From pmmp\utils Max Signed 32-bit
Limits::INT32_MIN, // From pmmp\utils Min Signed 32-bit
];
echo "--- 1. Testing Single IntTags (Works correctly) ---\n";
foreach ($values as $expected) {
$tag = CompoundTag::create()->setInt("val", $expected);
// Write -> Read cycle
$actual = ($serializer->read($serializer->write(new TreeRoot($tag))))
->mustGetCompoundTag()
->getInt("val");
echo "Input: $expected -> Output: $actual\n";
}
echo "\n--- 2. Testing IntArrayTag (Returns Unsigned) ---\n";
$tag = CompoundTag::create()->setIntArray("vals", $values);
// Write -> Read cycle
$actualArray = ($serializer->read($serializer->write(new TreeRoot($tag))))
->mustGetCompoundTag()
->getIntArray("vals");
foreach ($actualArray as $i => $actual) {
$expected = $values[$i];
echo "Input: $expected -> Output: $actual\n";
}outputs:
--- 1. Testing Single IntTags (Works correctly) ---
Input: 1337 -> Output: 1337
Input: -127 -> Output: -127
Input: 2147483647 -> Output: 2147483647
Input: -2147483648 -> Output: -2147483648
--- 2. Testing IntArrayTag (Returns Unsigned) ---
Input: 1337 -> Output: 1337
Input: -127 -> Output: 4294967169
Input: 2147483647 -> Output: 2147483647
Input: -2147483648 -> Output: 2147483648
My initial chain of thought:
My goal was to write LENativeNbtSerializer and LENativeNbtDeserializer (since ByteBuffer is split into reader/writer in ext-encoding). I was looking at the NBT specifications for Bedrock (where types are generally defined as Int16, Int32, etc.). Since NbtStreamReader explicitly provides methods like readSignedShort, I assumed the library intends to strictly adhere to signedness. However, seeing readIntArray returning unsigned values made me wonder if this is an intended performance trade-off (avoiding iteration for signInt) or an oversight, as it breaks arithmetic logic when expecting standard NBT behavior.
Environment
user@machine:~$ php -r "echo 'PHP: ' . PHP_VERSION . PHP_EOL; echo 'OS: ' . PHP_OS . PHP_EOL; echo 'Arch: ' . (PHP_INT_SIZE * 8) . '-bit' . PHP_EOL; echo 'Endianness: ' . (unpack('S', '\x01\x00')[1] === 1 ? 'Little' : 'Big') . PHP_EOL;"
PHP: 8.3.25
OS: Linux
Arch: 64-bit
Endianness: Big