fix: большой фикс недочетов и фикс пары язв
This commit is contained in:
@@ -119,60 +119,57 @@ Uint8List packPacket(int opcode, Map<dynamic, dynamic> payload, {int seq = 0}) {
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Распаковка пакета от сервера
|
||||
const int _isolateDecodeThreshold = 4096;
|
||||
|
||||
Future<Packet> unpackPacket(Uint8List packet) async {
|
||||
return Isolate.run(() {
|
||||
// Для удобства расшифровки пакета переводим в ByteData
|
||||
ByteData packetData = ByteData.view(
|
||||
packet.buffer,
|
||||
packet.offsetInBytes,
|
||||
packet.lengthInBytes,
|
||||
);
|
||||
final header = ByteData.sublistView(packet);
|
||||
|
||||
// API версия и cmd представляют из себя 8 битные числа
|
||||
final apiVer = packetData.getUint8(0) & 0xFF;
|
||||
final cmd = packetData.getUint8(1) & 0xFF;
|
||||
final apiVer = header.getUint8(0) & 0xFF;
|
||||
final cmd = header.getUint8(1) & 0xFF;
|
||||
final seq = header.getUint16(2) & 0xFFFF;
|
||||
final opcode = header.getUint16(4) & 0xFFFF;
|
||||
final packedLen = header.getUint32(6);
|
||||
final compFlag = packedLen >> 24;
|
||||
final payloadLength = packedLen & 0xFFFFFF;
|
||||
|
||||
// Sequence и OPCode представляют из себя 16 битные числа
|
||||
final seq = packetData.getUint16(2) & 0xFFFF;
|
||||
final opcode = packetData.getUint16(4) & 0xFFFF;
|
||||
if (payloadLength == 0) {
|
||||
return Packet(api: apiVer, cmd: cmd, seq: seq, opcode: opcode);
|
||||
}
|
||||
|
||||
// После базовых переменных идет длина пакета, является 32 битным числом
|
||||
final packedLen = packetData.getUint32(6);
|
||||
final end = headerSize + payloadLength;
|
||||
if (end > packet.length) {
|
||||
throw Exception('Packet payload length $payloadLength exceeds buffer');
|
||||
}
|
||||
final slice = Uint8List.sublistView(packet, headerSize, end);
|
||||
|
||||
// Compression flag показывает, сжат ли payload
|
||||
final compFlag = packedLen >> 24;
|
||||
dynamic payload;
|
||||
if (compFlag == 0 && slice.length < _isolateDecodeThreshold) {
|
||||
payload = _deserializePayload(slice, compFlag);
|
||||
} else {
|
||||
final owned = Uint8List.fromList(slice);
|
||||
payload = await Isolate.run(() => _deserializePayload(owned, compFlag));
|
||||
}
|
||||
|
||||
// Длина payload'а
|
||||
final payloadLength = packedLen & 0xFFFFFF;
|
||||
return Packet(
|
||||
api: apiVer,
|
||||
cmd: cmd,
|
||||
seq: seq,
|
||||
opcode: opcode,
|
||||
payload: payload,
|
||||
);
|
||||
}
|
||||
|
||||
// Байты payload'а, могут быть сжаты LZ4
|
||||
var payloadBytes = packet.buffer.asUint8List(10, payloadLength);
|
||||
|
||||
dynamic payload;
|
||||
|
||||
if (payloadBytes.isNotEmpty) {
|
||||
if (compFlag != 0) {
|
||||
payloadBytes = _decompressPayload(payloadBytes);
|
||||
}
|
||||
|
||||
try {
|
||||
payload = msgpack.deserialize(payloadBytes);
|
||||
} catch (e) {
|
||||
if (payloadBytes.isNotEmpty) {
|
||||
throw Exception("MsgPack deserialization error: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Packet(
|
||||
api: apiVer,
|
||||
cmd: cmd,
|
||||
seq: seq,
|
||||
opcode: opcode,
|
||||
payload: payload,
|
||||
);
|
||||
});
|
||||
dynamic _deserializePayload(Uint8List payloadBytes, int compFlag) {
|
||||
var bytes = payloadBytes;
|
||||
if (compFlag != 0) {
|
||||
bytes = _decompressPayload(bytes);
|
||||
}
|
||||
if (bytes.isEmpty) return null;
|
||||
try {
|
||||
return msgpack.deserialize(bytes);
|
||||
} catch (e) {
|
||||
throw Exception('MsgPack deserialization error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Определяет формат сжатия по magic-number и распаковывает payload.
|
||||
@@ -185,7 +182,7 @@ Uint8List _decompressPayload(Uint8List src) {
|
||||
src[2] == 0x2F &&
|
||||
src[3] == 0xFD) {
|
||||
try {
|
||||
return ZstdCodec().decompress(src);
|
||||
return ZstdCodec(maxDecompressedSize: _maxDecompressedSize).decompress(src);
|
||||
} catch (e) {
|
||||
throw Exception('Zstd decompression error: $e');
|
||||
}
|
||||
@@ -215,9 +212,23 @@ Uint8List _decompressPayload(Uint8List src) {
|
||||
/// LZ4 block декомпрессия (без frame-заголовка).
|
||||
/// Сервер шлёт именно block-формат, dart_lz4 его не поддерживает.
|
||||
Uint8List _lz4BlockDecompress(Uint8List src, int maxSize) {
|
||||
final dst = BytesBuilder(copy: false);
|
||||
var out = Uint8List(1024);
|
||||
int outLen = 0;
|
||||
int pos = 0;
|
||||
|
||||
void ensure(int extra) {
|
||||
if (outLen + extra > maxSize) throw StateError('LZ4: превышен лимит');
|
||||
if (outLen + extra <= out.length) return;
|
||||
var newCap = out.length * 2;
|
||||
while (newCap < outLen + extra) {
|
||||
newCap *= 2;
|
||||
}
|
||||
if (newCap > maxSize) newCap = maxSize;
|
||||
final grown = Uint8List(newCap);
|
||||
grown.setRange(0, outLen, out);
|
||||
out = grown;
|
||||
}
|
||||
|
||||
while (pos < src.length) {
|
||||
final token = src[pos++];
|
||||
var litLen = token >> 4;
|
||||
@@ -231,7 +242,9 @@ Uint8List _lz4BlockDecompress(Uint8List src, int maxSize) {
|
||||
}
|
||||
|
||||
if (litLen > 0) {
|
||||
dst.add(src.sublist(pos, pos + litLen));
|
||||
ensure(litLen);
|
||||
out.setRange(outLen, outLen + litLen, src, pos);
|
||||
outLen += litLen;
|
||||
pos += litLen;
|
||||
}
|
||||
|
||||
@@ -251,12 +264,14 @@ Uint8List _lz4BlockDecompress(Uint8List src, int maxSize) {
|
||||
}
|
||||
}
|
||||
|
||||
final out = dst.toBytes();
|
||||
final start = out.length - offset;
|
||||
dst.add(List<int>.generate(matchLen, (i) => out[start + (i % offset)]));
|
||||
|
||||
if (dst.length > maxSize) throw StateError('LZ4: превышен лимит');
|
||||
ensure(matchLen);
|
||||
final start = outLen - offset;
|
||||
if (start < 0) throw StateError('LZ4: offset за пределами вывода');
|
||||
for (var i = 0; i < matchLen; i++) {
|
||||
out[outLen + i] = out[start + i];
|
||||
}
|
||||
outLen += matchLen;
|
||||
}
|
||||
|
||||
return dst.toBytes();
|
||||
return Uint8List.sublistView(out, 0, outLen);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user