mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-08-08 06:46:11 +00:00
125 lines
4.4 KiB
Kotlin
125 lines
4.4 KiB
Kotlin
package com.bitchat.android.model
|
|
|
|
import com.bitchat.android.sync.SyncDefaults
|
|
|
|
/**
|
|
* REQUEST_SYNC payload using GCS (Golomb-Coded Set) parameters.
|
|
* TLV (type, length16, value), types:
|
|
* - 0x01: P (uint8) — Golomb-Rice parameter
|
|
* - 0x02: M (uint32, big-endian) — hash range (N * 2^P)
|
|
* - 0x03: data (opaque) — GR bitstream bytes
|
|
*/
|
|
data class RequestSyncPacket(
|
|
val p: Int,
|
|
val m: Long,
|
|
val data: ByteArray,
|
|
val wantedTypes: List<UByte>? = null,
|
|
val minTimestamp: ULong? = null
|
|
) {
|
|
fun encode(): ByteArray {
|
|
val out = ArrayList<Byte>()
|
|
fun putTLV(t: Int, v: ByteArray) {
|
|
out.add(t.toByte())
|
|
val len = v.size
|
|
out.add(((len ushr 8) and 0xFF).toByte())
|
|
out.add((len and 0xFF).toByte())
|
|
out.addAll(v.toList())
|
|
}
|
|
// P
|
|
putTLV(0x01, byteArrayOf(p.toByte()))
|
|
// M (uint32)
|
|
val m32 = m.coerceAtMost(0xffff_ffffL)
|
|
putTLV(
|
|
0x02,
|
|
byteArrayOf(
|
|
((m32 ushr 24) and 0xFF).toByte(),
|
|
((m32 ushr 16) and 0xFF).toByte(),
|
|
((m32 ushr 8) and 0xFF).toByte(),
|
|
(m32 and 0xFF).toByte()
|
|
)
|
|
)
|
|
// data
|
|
putTLV(0x03, data)
|
|
// wantedTypes
|
|
wantedTypes?.let { types ->
|
|
if (types.isNotEmpty()) {
|
|
val typesBytes = types.map { it.toByte() }.toByteArray()
|
|
putTLV(0x04, typesBytes)
|
|
}
|
|
}
|
|
// minTimestamp
|
|
minTimestamp?.let { ts ->
|
|
val tsLong = ts.toLong()
|
|
val tsBytes = byteArrayOf(
|
|
((tsLong ushr 56) and 0xFF).toByte(),
|
|
((tsLong ushr 48) and 0xFF).toByte(),
|
|
((tsLong ushr 40) and 0xFF).toByte(),
|
|
((tsLong ushr 32) and 0xFF).toByte(),
|
|
((tsLong ushr 24) and 0xFF).toByte(),
|
|
((tsLong ushr 16) and 0xFF).toByte(),
|
|
((tsLong ushr 8) and 0xFF).toByte(),
|
|
(tsLong and 0xFF).toByte()
|
|
)
|
|
putTLV(0x05, tsBytes)
|
|
}
|
|
return out.toByteArray()
|
|
}
|
|
|
|
companion object {
|
|
// Receiver-side safety limit (configurable constant)
|
|
const val MAX_ACCEPT_FILTER_BYTES: Int = SyncDefaults.MAX_ACCEPT_FILTER_BYTES
|
|
|
|
fun decode(data: ByteArray): RequestSyncPacket? {
|
|
var off = 0
|
|
var p: Int? = null
|
|
var m: Long? = null
|
|
var payload: ByteArray? = null
|
|
var wantedTypes: List<UByte>? = null
|
|
var minTimestamp: ULong? = null
|
|
|
|
while (off + 3 <= data.size) {
|
|
val t = (data[off].toInt() and 0xFF); off += 1
|
|
val len = ((data[off].toInt() and 0xFF) shl 8) or (data[off+1].toInt() and 0xFF); off += 2
|
|
if (off + len > data.size) return null
|
|
val v = data.copyOfRange(off, off + len); off += len
|
|
when (t) {
|
|
0x01 -> if (len == 1) p = (v[0].toInt() and 0xFF)
|
|
0x02 -> if (len == 4) {
|
|
val mm = ((v[0].toLong() and 0xFF) shl 24) or
|
|
((v[1].toLong() and 0xFF) shl 16) or
|
|
((v[2].toLong() and 0xFF) shl 8) or
|
|
(v[3].toLong() and 0xFF)
|
|
m = mm
|
|
}
|
|
0x03 -> {
|
|
if (v.size > MAX_ACCEPT_FILTER_BYTES) return null
|
|
payload = v
|
|
}
|
|
0x04 -> {
|
|
val typesList = mutableListOf<UByte>()
|
|
for (b in v) {
|
|
typesList.add(b.toUByte())
|
|
}
|
|
wantedTypes = typesList
|
|
}
|
|
0x05 -> {
|
|
if (len == 8) {
|
|
var tsVal = 0L
|
|
for (i in 0 until 8) {
|
|
tsVal = (tsVal shl 8) or (v[i].toLong() and 0xFF)
|
|
}
|
|
minTimestamp = tsVal.toULong()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
val pp = p ?: return null
|
|
val mm = m ?: return null
|
|
val dd = payload ?: return null
|
|
if (pp < 1 || mm <= 0L) return null
|
|
return RequestSyncPacket(pp, mm, dd, wantedTypes, minTimestamp)
|
|
}
|
|
}
|
|
}
|