4 changed files with 189 additions and 7 deletions
@ -0,0 +1,62 @@
|
||||
package com.utun.chat.data |
||||
|
||||
import java.nio.ByteBuffer |
||||
import java.nio.ByteOrder |
||||
|
||||
/** Форматы событий общего ядра; все идентификаторы сохраняют 64 бита, включая старший бит. */ |
||||
object AvatarProtocol { |
||||
const val MAX_USER = 1024 * 1024 |
||||
const val MAX_GROUP = 16 * 1024 |
||||
private val filePattern = Regex("[0-9a-f]{64}\\.png") |
||||
fun validFile(file: String): Boolean = filePattern.matches(file) |
||||
fun node(id: String): Long = id.toULongOrNull(16)?.toLong() ?: 0 |
||||
fun hex(id: Long): String = id.toULong().toString(16).padStart(16, '0') |
||||
fun hash(bytes: ByteArray): String = bytes.joinToString("") { "%02x".format(it.toInt() and 255) } |
||||
private fun buffer(data: ByteArray) = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN) |
||||
|
||||
data class Group(val id: Long, val group: Long, val version: Long, val hash: String, |
||||
val canEdit: Boolean, val failed: Boolean, val image: ByteArray) |
||||
fun group(data: ByteArray): Group { |
||||
require(data.size in 60..60 + MAX_GROUP) { "group snapshot length=${data.size}" } |
||||
val b = buffer(data) |
||||
val id = b.long; val group = b.long; val version = b.long |
||||
val hash = ByteArray(32); b.get(hash) |
||||
val canEdit = b.get().toInt(); val failed = b.get().toInt() |
||||
val size = b.short.toInt() and 65535 |
||||
require(canEdit in 0..1 && failed in 0..1 && size == b.remaining()) { "invalid group snapshot header" } |
||||
return Group(id, group, version, hash(hash), canEdit != 0, failed != 0, ByteArray(size).also { b.get(it) }) |
||||
} |
||||
|
||||
data class Reply(val id: Long, val group: Long, val server: Long = 0, val owner: Long = 0, |
||||
val command: Int = 0, val status: Int = 0, val size: Long = 0, val hash: String = "") |
||||
fun response(data: ByteArray): Reply { |
||||
require(data.size >= 141) { "file response length=${data.size}" } |
||||
val b = buffer(data) |
||||
val group = b.long; val server = b.long |
||||
require(b.get().toInt() == 4 && b.get().toInt() == 2) { "invalid file response protocol/kind" } |
||||
val command = b.get().toInt() and 255; val status = b.short.toInt() and 65535 |
||||
val id = b.long; val owner = b.long |
||||
b.long; val size = b.long |
||||
b.position(16 + 77); val hash = ByteArray(32); b.get(hash) |
||||
b.position(16 + 119) |
||||
val path = b.short.toInt() and 65535; val target = b.short.toInt() and 65535; val payload = b.short.toInt() and 65535 |
||||
require(path <= 1024 && target <= 1024 && payload <= 8192 && b.remaining() == path + target + payload) { |
||||
"invalid file response body lengths" |
||||
} |
||||
return Reply(id, group, server, owner, command, status, size, hash(hash)) |
||||
} |
||||
fun transfer(data: ByteArray): Reply? { |
||||
require(data.size == 51) { "transfer length=${data.size}" } |
||||
val b = buffer(data) |
||||
val id = b.long; val group = b.long; val server = b.long; val owner = b.long |
||||
val done = b.long; val total = b.long; val status = b.short.toInt() and 65535 |
||||
val state = b.get().toInt() |
||||
require(state in 1..2 && done >= 0 && total >= 0 && done <= total) { "invalid transfer progress" } |
||||
return if (state == 2) Reply(id, group, server, owner, status = status, size = total) else null |
||||
} |
||||
fun published(data: ByteArray): Reply { |
||||
require(data.size == 17 && data[16].toInt() in 0..1) { "invalid publication result" } |
||||
val b = buffer(data) |
||||
return Reply(b.long, b.long, status = b.get().toInt()) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue