charm/arraybuffer

由FFI支持的struct类型数组。为JavaScript提供了UInt8Array、UInt16Array、UInt32Array、Float64Array和ArrayBuffer的等价物。

1.0.4 2022-05-24 10:16 UTC

This package is auto-updated.

Last update: 2024-08-24 15:09:29 UTC


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