charm / arraybuffer
由FFI支持的struct类型数组。为JavaScript提供了UInt8Array、UInt16Array、UInt32Array、Float64Array和ArrayBuffer的等价物。
1.0.4
2022-05-24 10:16 UTC
Requires (Dev)
- phpunit/phpunit: ^9.5
README
一个基于javascript ArrayBuffer的强大类。该库的性能比使用pack()
和unpack()
要快得多,归功于PHP 7.4 FFI。
当你分配一个ArrayBuffer时,你可以直接以各种强类型读取和写入内存区域的单个值
ArrayBuffer::getUInt8View()
会给你一个无符号8位整数的数组。对于16、32和64位无符号整数也存在相应的函数。ArrayBuffer::getFloat32View()
会给你一个32位浮点数的数组,你可以使用类似的方法来使用64位方法。
基本用法
$someString = 'Hello World!';
$buffer = Charm\ArrayBuffer::fromString($someString);
// get an Uint8View
$uint8array = $buffer->uint8;
// access as an unsigned 8 bit integer
echo chr($buffer->uint8[2])."\n"; // 108' - the ascii value of the third character in the string
// access as single characters
echo $buffer->char[2]."\n"; // 'l'
// access as a 16 bit signed integer
echo $buffer->int16[0]."\n";
// update the previous integer by modifying byte 0
$buffer->int8[1] = 0;
echo $buffer->int16[0]."\n";
小端和大端
小端意味着整数或浮点值的“小端”首先存储。这意味着存储的第一个是最低有效字节。
当你从缓冲区访问数据时,数据将以CPU默认的端序进行读取和写入。
如果计算机使用大端,而你正在处理的是小端格式的文件或网络流 - 你可以在获取数据后转换整数和浮点数的端序。
// putting 0xFF00 in the memory slots
$buffer->uint[0] = 255;
$buffer->uint[1] = 0;
// reading a 2 byte integer
if ($buffer->uint16[0] === 0xFF00) {
echo "Big endian computer\n";
} else {
echo "Little endian computer\n";
}
如果你的计算机与缓冲区相比具有错误的端序,你必须在写入和读取之前进行转换。
$value = $buffer->uint16->flip($buffer->uint16[0]);
更复杂的数据
通过声明一个类似于下面的复杂类型的struct
,可以最轻松地完成复杂二进制数据的解析
/**
* We've received some binary data, so we create an ArrayBuffer
*/
$buffer = Charm\ArrayBuffer::fromString($someBinaryData);
/**
* We would like to work with the data more efficiently, so we declare a normal `FFI\CType` object.
* You may want to make these types using a more traditional C-style header file (.h) and invoke
* {@see FFI::type()} yourself.
*/
$headerType = $buffer->createStruct([
'marker' => 'char[5]', // 5 bytes are reserved for a header string
'packetType' => 'uint8_t', // 1 byte for the packet type
'reserved0' => 'char[2]', // 2 bytes skipped
'sender_ipv4' => 'uint8_t[4]', // 4 bytes for the IPv4 address of the sender
'receiver_ipv4' => 'uint8_t[4]', // 4 bytes for the IPv4 address of the receiver
'reserved1' => 'char[48]', // 48 bytes skipped to make the header packet 128 bytes total
'timestamp' => 'uint64_t', // a 64 bit integer for the unix timestamp
]);
/**
* Use the struct to get a view from the ArrayBuffer at offset 0.
*/
$header = $buffer->getStructView($headerType, 0);
/**
* Read properties. The properties map directly to the underlying ArrayBuffer, so
* if the buffer is modified through other views, it will be reflected here
* instantly.
*/
if ($header->marker != 'PACKT') {
throw \Exception("Packet is not the right type");
}
/**
* Write properties. As mentioned, this will update the underlying ArrayBuffer
* and all other views regardless of format.
*/
$header->packetType = 42;
$header->sender_ipv4[0] = 127;
$header->sender_ipv4[1] = 0;
$header->sender_ipv4[2] = 0;
$header->sender_ipv4[3] = 1;
$header->receiver_ipv4[0] = 127;
$header->receiver_ipv4[1] = 0;
$header->receiver_ipv4[2] = 0;
$header->receiver_ipv4[3] = 1;
$header->timestamp = time();
/**
* To demonstrate the memory mapping, we can access packetType directly if we wish.
*/
var_dump($buffer->uint8[5]); // 42