feat: support Columba location metadata

This commit is contained in:
torlando-agent[bot]
2026-08-07 01:23:08 +00:00
parent 3f5d386c0a
commit 7637e28863
4 changed files with 391 additions and 0 deletions
@@ -13,6 +13,8 @@ constexpr std::size_t MAX_LOCATION_ELEMENTS = 16;
constexpr std::size_t MAX_SKIP_DEPTH = 8;
constexpr std::size_t MAX_SKIP_ITEMS = 64;
constexpr std::size_t MAX_ENCODED_TELEMETRY = 96;
constexpr std::size_t MAX_CUSTOM_META_ENTRIES = 16;
constexpr std::size_t MAX_ENCODED_CUSTOM_META = 128;
class Cursor {
public:
@@ -64,6 +66,45 @@ public:
return true;
}
bool readBoolean(bool& value) {
uint8_t marker = 0;
if (!readByte(marker)) return false;
if (marker == 0xc2U) {
value = false;
return true;
}
if (marker == 0xc3U) {
value = true;
return true;
}
return false;
}
bool readString(BinaryView& value) {
uint8_t marker = 0;
if (!readByte(marker)) return false;
std::size_t length = 0;
if ((marker & 0xe0U) == 0xa0U) {
length = marker & 0x1fU;
} else if (marker == 0xd9U) {
uint8_t byte_length = 0;
if (!readByte(byte_length)) return false;
length = byte_length;
} else if (marker == 0xdaU) {
if (!readBigEndianSize(2, length)) return false;
} else if (marker == 0xdbU) {
if (!readBigEndianSize(4, length)) return false;
} else {
return false;
}
const uint8_t* bytes = nullptr;
if (!readBytes(length, bytes)) return false;
value = BinaryView{bytes, length};
return true;
}
bool readMapSize(std::size_t& count) {
uint8_t marker = 0;
if (!readByte(marker)) return false;
@@ -262,6 +303,23 @@ public:
return writeByte(0xcfU) && writeBigEndian(value, 8);
}
bool writeBoolean(bool value) {
return writeByte(value ? 0xc3U : 0xc2U);
}
bool writeString(const char* value, std::size_t length) {
if (value == nullptr) return false;
if (length <= 31U) {
return writeByte(static_cast<uint8_t>(0xa0U | length)) &&
writeBytes(reinterpret_cast<const uint8_t*>(value), length);
}
if (length <= 0xffU) {
return writeByte(0xd9U) && writeByte(static_cast<uint8_t>(length)) &&
writeBytes(reinterpret_cast<const uint8_t*>(value), length);
}
return false;
}
bool writeBinary(const uint8_t* data, std::size_t size) {
if (size > 0xffU) return false;
return writeByte(0xc4U) && writeByte(static_cast<uint8_t>(size)) &&
@@ -355,6 +413,12 @@ bool locationInRange(const LocationTelemetry& location) {
location.longitude_e6 <= 180000000;
}
bool stringEquals(const BinaryView& value, const char* expected,
std::size_t expected_size) {
return value.size == expected_size &&
std::memcmp(value.data, expected, expected_size) == 0;
}
} // namespace
FieldValueResult unwrapLxmfBinaryFieldValue(
@@ -501,4 +565,100 @@ EncodeResult encodeLocationTelemetry(
return EncodeResult::OK;
}
CustomMetaResult decodeCustomLocationMeta(
const uint8_t* data,
std::size_t size,
CustomLocationMeta& output) {
if (data == nullptr || size == 0) return CustomMetaResult::INVALID_ARGUMENT;
Cursor cursor(data, size);
std::size_t map_size = 0;
if (!cursor.readMapSize(map_size) || map_size > MAX_CUSTOM_META_ENTRIES) {
return CustomMetaResult::MALFORMED;
}
CustomLocationMeta candidate{};
std::size_t skip_budget = MAX_SKIP_ITEMS;
for (std::size_t index = 0; index < map_size; ++index) {
BinaryView key{};
if (!cursor.readString(key)) return CustomMetaResult::MALFORMED;
if (stringEquals(key, "cease", 5)) {
if (candidate.has_cease || !cursor.readBoolean(candidate.cease)) {
return CustomMetaResult::MALFORMED;
}
candidate.has_cease = true;
} else if (stringEquals(key, "expires", 7)) {
if (candidate.has_expires ||
!cursor.readUnsigned(candidate.expires_millis)) {
return CustomMetaResult::MALFORMED;
}
candidate.has_expires = true;
} else if (stringEquals(key, "approxRadius", 12)) {
uint64_t radius = 0;
if (candidate.has_approx_radius || !cursor.readUnsigned(radius) ||
radius > std::numeric_limits<uint32_t>::max()) {
return CustomMetaResult::MALFORMED;
}
candidate.approx_radius_meters = static_cast<uint32_t>(radius);
candidate.has_approx_radius = true;
} else if (stringEquals(key, "ts", 2)) {
if (candidate.has_timestamp ||
!cursor.readUnsigned(candidate.timestamp_millis)) {
return CustomMetaResult::MALFORMED;
}
candidate.has_timestamp = true;
} else if (!cursor.skipValue(0, skip_budget)) {
return CustomMetaResult::MALFORMED;
}
}
if (!cursor.atEnd()) return CustomMetaResult::MALFORMED;
output = candidate;
return CustomMetaResult::OK;
}
CustomMetaResult encodeCustomLocationMeta(
const CustomLocationMeta& input,
uint8_t* output,
std::size_t capacity,
std::size_t& written) {
const std::size_t field_count =
static_cast<std::size_t>(input.has_cease) +
static_cast<std::size_t>(input.has_expires) +
static_cast<std::size_t>(input.has_approx_radius) +
static_cast<std::size_t>(input.has_timestamp);
if (field_count == 0) {
written = 0;
return CustomMetaResult::EMPTY;
}
if (output == nullptr) return CustomMetaResult::INVALID_ARGUMENT;
uint8_t temporary[MAX_ENCODED_CUSTOM_META]{};
Writer writer(temporary, sizeof(temporary));
bool ok = writer.writeByte(static_cast<uint8_t>(0x80U | field_count));
if (input.has_cease) {
ok = ok && writer.writeString("cease", 5) &&
writer.writeBoolean(input.cease);
}
if (input.has_expires) {
ok = ok && writer.writeString("expires", 7) &&
writer.writeUnsigned(input.expires_millis);
}
if (input.has_approx_radius) {
ok = ok && writer.writeString("approxRadius", 12) &&
writer.writeUnsigned(input.approx_radius_meters);
}
if (input.has_timestamp) {
ok = ok && writer.writeString("ts", 2) &&
writer.writeUnsigned(input.timestamp_millis);
}
if (!ok) return CustomMetaResult::INVALID_ARGUMENT;
if (capacity < writer.size()) return CustomMetaResult::BUFFER_TOO_SMALL;
std::memcpy(output, temporary, writer.size());
written = writer.size();
return CustomMetaResult::OK;
}
} // namespace Telemetry