%PDF- %PDF-
| Direktori : /proc/thread-self/root/www/varak.net/nextcloud.varak.net/apps_old/apps/text/lib/ |
| Current File : //proc/thread-self/root/www/varak.net/nextcloud.varak.net/apps_old/apps/text/lib/YjsMessage.php |
<?php
/**
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\Text;
use InvalidArgumentException;
/**
* Steps are base64 encoded messages of the yjs protocols
* https://github.com/yjs/y-protocols
*
* This class is a simple representation of a message containing some methods
* to decode parts of it for what we need on the backend
*
* Relevant resources:
* https://github.com/yjs/y-protocols/blob/master/PROTOCOL.md
* https://github.com/yjs/y-websocket/blob/master/src/y-websocket.js#L19-L22
* https://github.com/yjs/y-protocols/blob/master/sync.js#L38-L40
* https://github.com/dmonad/lib0/blob/master/decoding.js
*/
class YjsMessage {
public const YJS_MESSAGE_SYNC = 0;
public const YJS_MESSAGE_AWARENESS = 1;
public const YJS_MESSAGE_AWARENESS_QUERY = 3;
public const YJS_MESSAGE_SYNC_STEP1 = 0;
public const YJS_MESSAGE_SYNC_STEP2 = 1;
public const YJS_MESSAGE_SYNC_UPDATE = 2;
private int $pos = 0;
public function __construct(
private string $data = ''
) {
}
public static function fromBase64(string $data = ''): self {
return new self(base64_decode($data));
}
/**
* https://github.com/dmonad/lib0/blob/bd69ab4dc701d77e808f2bab08d96d63acd297da/decoding.js#L242
*/
public function readVarUint(): int {
$bytes = array_values(unpack('C*', $this->data));
$num = 0;
$mult = 1;
$len = count($bytes);
while ($this->pos < $len) {
$r = $bytes[$this->pos++];
// num = num | ((r & binary.BITS7) << len)
$num = $num + ($r & 0b1111111) * $mult;
$mult *= 128;
if ($r <= 0b1111111) {
return $num;
}
// Number.MAX_SAFE_INTEGER in JS
if ($num > 9007199254740990) {
throw new \OutOfBoundsException();
}
}
throw new InvalidArgumentException();
}
public function getYjsMessageType(): int {
$oldPos = $this->pos;
$this->pos = 0;
$messageType = $this->readVarUint();
$this->pos = $oldPos;
return $messageType;
}
public function getYjsSyncType(): int {
$oldPos = $this->pos;
$this->pos = 0;
$messageType = $this->readVarUint();
if ($messageType !== self::YJS_MESSAGE_SYNC) {
throw new \ValueError('Message is not a sync message');
}
$syncType = $this->readVarUint();
$this->pos = $oldPos;
return $syncType;
}
/**
* Based on https://github.com/yjs/y-protocols/blob/master/PROTOCOL.md#handling-read-only-users
*/
public function isUpdate(): bool {
if ($this->getYjsMessageType() === self::YJS_MESSAGE_SYNC) {
if (in_array($this->getYjsSyncType(), [self::YJS_MESSAGE_SYNC_STEP2, self::YJS_MESSAGE_SYNC_UPDATE])) {
return true;
}
}
return false;
}
}