Refactor data handling of contacts (#267)

* Refactor data handling in MeshCoreConnector and BufferReader for improved readability and efficiency

* Update lib/connector/meshcore_connector.dart

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix pointer tracking in BufferReader's readCString method

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Winston Lowe
2026-03-07 01:23:46 -08:00
committed by GitHub
parent eba95af31f
commit c2671ac2ae
3 changed files with 76 additions and 44 deletions
+29 -24
View File
@@ -3820,40 +3820,40 @@ class MeshCoreConnector extends ChangeNotifier {
void _handleRxData(Uint8List frame) { void _handleRxData(Uint8List frame) {
final packet = BufferReader(frame); final packet = BufferReader(frame);
double snr = 0.0;
int routeType = 0;
int payloadType = 0;
Uint8List pathBytes = Uint8List(0);
Uint8List payload = Uint8List(0);
try { try {
packet.skipBytes(1); // Skip frame type byte packet.skipBytes(1); // Skip frame type byte
snr = packet.readInt8() / 4.0; final snr = packet.readInt8() / 4.0;
packet.skipBytes(1); // Skip RSSI byte packet.skipBytes(1); // Skip RSSI byte
//final rssi = packet.readByte(); //final rssi = packet.readByte();
final header = packet.readByte(); final header = packet.readByte();
routeType = header & 0x03; final routeType = header & 0x03;
payloadType = (header >> 2) & 0x0F; final payloadType = (header >> 2) & 0x0F;
if (routeType == _routeTransportFlood ||
routeType == _routeTransportDirect) {
packet.skipBytes(4); // Skip transport-specific bytes
}
//final payloadVer = (header >> 6) & 0x03; //final payloadVer = (header >> 6) & 0x03;
final pathLen = packet.readByte(); final pathLen = packet.readByte();
pathBytes = packet.readBytes(pathLen); final pathBytes = packet.readBytes(pathLen);
payload = packet.readBytes(packet.remaining); final payload = packet.readBytes(packet.remaining);
final rawPacket = frame.sublist(3);
switch (payloadType) {
case payloadTypeADVERT:
_handlePayloadAdvertReceived(
rawPacket,
payload,
pathBytes,
routeType,
snr,
);
break;
default:
}
} catch (e) { } catch (e) {
appLogger.warn('Malformed RX frame: $e', tag: 'Connector'); appLogger.warn('Malformed RX frame: $e', tag: 'Connector');
return; return;
} }
final rawPacket = frame.sublist(3);
switch (payloadType) {
case payloadTypeADVERT:
_handlePayloadAdvertReceived(
rawPacket,
payload,
pathBytes,
routeType,
snr,
);
break;
default:
}
} }
void importContact(Uint8List frame) { void importContact(Uint8List frame) {
@@ -3865,7 +3865,12 @@ class MeshCoreConnector extends ChangeNotifier {
packet.skipBytes(1); // Skip SNR byte packet.skipBytes(1); // Skip SNR byte
packet.skipBytes(1); // Skip RSSI byte packet.skipBytes(1); // Skip RSSI byte
final header = packet.readByte(); final header = packet.readByte();
final routeType = header & 0x03;
payloadType = (header >> 2) & 0x0F; payloadType = (header >> 2) & 0x0F;
if (routeType == _routeTransportFlood ||
routeType == _routeTransportDirect) {
packet.skipBytes(4); // Skip transport-specific bytes
}
//final payloadVer = (header >> 6) & 0x03; //final payloadVer = (header >> 6) & 0x03;
final pathLen = packet.readByte(); final pathLen = packet.readByte();
pathBytes = packet.readBytes(pathLen); pathBytes = packet.readBytes(pathLen);
@@ -3915,7 +3920,7 @@ class MeshCoreConnector extends ChangeNotifier {
publicKey: publicKey, publicKey: publicKey,
name: name, name: name,
type: type, type: type,
pathLength: pathBytes.length, pathLength: pathBytes.isEmpty ? -1 : pathBytes.length,
path: Uint8List.fromList( path: Uint8List.fromList(
pathBytes.reversed.toList(), pathBytes.reversed.toList(),
), // Store path in reverse for easier use in outgoing messages ), // Store path in reverse for easier use in outgoing messages
+27 -1
View File
@@ -4,6 +4,7 @@ import 'dart:typed_data';
// Buffer Reader - sequential binary data reader with pointer tracking // Buffer Reader - sequential binary data reader with pointer tracking
class BufferReader { class BufferReader {
int _pointer = 0; int _pointer = 0;
int _lastPointer = 0;
final Uint8List _buffer; final Uint8List _buffer;
BufferReader(Uint8List data) : _buffer = Uint8List.fromList(data); BufferReader(Uint8List data) : _buffer = Uint8List.fromList(data);
@@ -13,6 +14,7 @@ class BufferReader {
int readByte() => readBytes(1)[0]; int readByte() => readBytes(1)[0];
Uint8List readBytes(int count) { Uint8List readBytes(int count) {
_lastPointer = _pointer;
if (_pointer + count > _buffer.length) { if (_pointer + count > _buffer.length) {
throw RangeError( throw RangeError(
'Attempted to read $count bytes at offset $_pointer, but only $remaining bytes remaining in buffer of length ${_buffer.length}', 'Attempted to read $count bytes at offset $_pointer, but only $remaining bytes remaining in buffer of length ${_buffer.length}',
@@ -24,6 +26,7 @@ class BufferReader {
} }
void skipBytes(int count) { void skipBytes(int count) {
_lastPointer = _pointer;
if (_pointer + count > _buffer.length) { if (_pointer + count > _buffer.length) {
throw RangeError( throw RangeError(
'Attempted to skip $count bytes at offset $_pointer, but only $remaining bytes remaining in buffer of length ${_buffer.length}', 'Attempted to skip $count bytes at offset $_pointer, but only $remaining bytes remaining in buffer of length ${_buffer.length}',
@@ -35,6 +38,7 @@ class BufferReader {
Uint8List readRemainingBytes() => readBytes(remaining); Uint8List readRemainingBytes() => readBytes(remaining);
String readString() { String readString() {
_lastPointer = _pointer;
final value = readRemainingBytes(); final value = readRemainingBytes();
try { try {
return utf8.decode(Uint8List.fromList(value), allowMalformed: true); return utf8.decode(Uint8List.fromList(value), allowMalformed: true);
@@ -43,7 +47,8 @@ class BufferReader {
} }
} }
String readCString(int maxLength) { String readCStringGreedy(int maxLength) {
_lastPointer = _pointer;
final value = <int>[]; final value = <int>[];
final bytes = readBytes(maxLength); final bytes = readBytes(maxLength);
for (final byte in bytes) { for (final byte in bytes) {
@@ -57,6 +62,24 @@ class BufferReader {
} }
} }
String readCString(int maxLength) {
final backupPointer = _pointer;
final value = <int>[];
int counter = 0;
while (counter < maxLength) {
final byte = readByte();
if (byte == 0) break;
value.add(byte);
counter++;
}
_lastPointer = backupPointer;
try {
return utf8.decode(Uint8List.fromList(value), allowMalformed: true);
} catch (e) {
return String.fromCharCodes(value); // Latin-1 fallback
}
}
int readUInt8() => readBytes(1).buffer.asByteData().getUint8(0); int readUInt8() => readBytes(1).buffer.asByteData().getUint8(0);
int readInt8() => readBytes(1).buffer.asByteData().getInt8(0); int readInt8() => readBytes(1).buffer.asByteData().getInt8(0);
int readUInt16LE() => int readUInt16LE() =>
@@ -78,6 +101,9 @@ class BufferReader {
if ((value & 0x800000) != 0) value -= 0x1000000; if ((value & 0x800000) != 0) value -= 0x1000000;
return value; return value;
} }
void resetPointer() => _pointer = 0;
void rewind() => _pointer = _lastPointer;
} }
// Buffer Writer - accumulating binary data builder // Buffer Writer - accumulating binary data builder
+20 -19
View File
@@ -1,4 +1,6 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:meshcore_open/utils/app_logger.dart';
import '../connector/meshcore_protocol.dart'; import '../connector/meshcore_protocol.dart';
class Contact { class Contact {
@@ -166,28 +168,27 @@ class Contact {
static Contact? fromFrame(Uint8List data) { static Contact? fromFrame(Uint8List data) {
if (data.isEmpty) return null; if (data.isEmpty) return null;
if (data[0] != respCodeContact) return null; final reader = BufferReader(data);
try { try {
final pubKey = Uint8List.fromList( final respCode = reader.readByte();
data.sublist(contactPubKeyOffset, contactPubKeyOffset + pubKeySize), if (respCode != respCodeContact && respCode != pushCodeNewAdvert) {
); return null;
final type = data[contactTypeOffset]; }
final flags = data[contactFlagsOffset]; final pubKey = reader.readBytes(pubKeySize);
final pathLen = data[contactPathLenOffset].toSigned(8); final type = reader.readByte();
final flags = reader.readByte();
final pathLen = reader.readByte();
final safePathLen = pathLen > 0 final safePathLen = pathLen > 0
? (pathLen > maxPathSize ? maxPathSize : pathLen) ? (pathLen > maxPathSize ? maxPathSize : pathLen)
: 0; : 0;
final pathBytes = safePathLen > 0 final pathBytes = reader.readBytes(maxPathSize).sublist(0, safePathLen);
? Uint8List.fromList( final name = reader.readCStringGreedy(maxNameSize);
data.sublist(contactPathOffset, contactPathOffset + safePathLen),
) final lastMod = reader.readUInt32LE();
: Uint8List(0);
final name = readCString(data, contactNameOffset, maxNameSize);
final lastmod = readUint32LE(data, contactLastModOffset);
double? lat, lon; double? lat, lon;
final latRaw = readInt32LE(data, contactLatOffset); final latRaw = reader.readInt32LE();
final lonRaw = readInt32LE(data, contactLonOffset); final lonRaw = reader.readInt32LE();
if (latRaw != 0 || lonRaw != 0) { if (latRaw != 0 || lonRaw != 0) {
lat = latRaw / 1e6; lat = latRaw / 1e6;
lon = lonRaw / 1e6; lon = lonRaw / 1e6;
@@ -198,14 +199,14 @@ class Contact {
name: name.isEmpty ? 'Unknown' : name, name: name.isEmpty ? 'Unknown' : name,
type: type, type: type,
flags: flags, flags: flags,
pathLength: pathLen, pathLength: pathLen > 0 ? (pathLen > maxPathSize ? -1 : pathLen) : -1,
path: pathBytes, path: pathBytes,
latitude: lat, latitude: lat,
longitude: lon, longitude: lon,
lastSeen: DateTime.fromMillisecondsSinceEpoch(lastmod * 1000), lastSeen: DateTime.fromMillisecondsSinceEpoch(lastMod * 1000),
); );
} catch (e) { } catch (e) {
// If parsing fails, return null appLogger.error('Failed to parse contact frame: $e');
return null; return null;
} }
} }