/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef LinkState_H
#define LinkState_H
#include "benc/String.h"
#include "util/VarInt.h"
#include "wire/Message.h"
#include "wire/Announce.h"
#include
#include
#define LinkState_SLOTS 18
struct LinkState {
uint16_t lagSlots[LinkState_SLOTS];
uint16_t dropSlots[LinkState_SLOTS];
uint32_t kbRecvSlots[LinkState_SLOTS];
uint32_t samples;
uint16_t nodeId;
uint16_t _pad;
};
static inline int LinkState_encode(
struct Message* msg,
struct LinkState* ls,
uint32_t lastSamples)
{
// Only encode the message if there is at least 255 bytes of headspace
// We can then encode as many as possible and finally pop one which spills over the
// size limit and then encode it again in the next message.
if (msg->padding < 255) { return 1; }
struct VarInt_Iter iter = {
.ptr = msg->bytes,
.end = msg->bytes,
.start = &msg->bytes[-msg->padding]
};
// Take the newest X entries where X = MIN(ls->samples - lastSamples, LinkState_SLOTS)
uint32_t i = ls->samples - 1;
int count = 0;
int err = 0;
for (; i >= lastSamples && count < LinkState_SLOTS; i--, count++) {
int idx = i % LinkState_SLOTS;
err |= VarInt_push(&iter, ls->kbRecvSlots[idx]);
err |= VarInt_push(&iter, ls->dropSlots[idx]);
err |= VarInt_push(&iter, ls->lagSlots[idx]);
}
if (err) { return 1; }
if (!count) { return 0; }
// index of the first slot which should be updated when parsing
Assert_true(!VarInt_push(&iter, (i + 1) % LinkState_SLOTS));
Assert_true(!VarInt_push(&iter, ls->nodeId));
int beginLength = msg->length;
Er_assert(Message_eshift(msg, (msg->bytes - iter.ptr)));
Assert_true(msg->bytes == iter.ptr);
int padCount = 0;
while ((uintptr_t)(&msg->bytes[-3]) & 7) {
Er_assert(Message_epush8(msg, 0));
padCount++;
}
Er_assert(Message_epush8(msg, padCount));
Er_assert(Message_epush8(msg, Announce_Type_LINK_STATE));
int finalLength = msg->length - beginLength;
Er_assert(Message_epush8(msg, finalLength + 1));
Assert_true(!(((uintptr_t)msg->bytes) & 7));
return 0;
}
static inline int LinkState_mkDecoder(struct Message* msg, struct VarInt_Iter* it)
{
if (!msg->length) { return 1; }
uint8_t len = msg->bytes[0];
if (msg->length < len) { return 1; }
if (len < 3) { return 1; }
it->ptr = &msg->bytes[1];
it->start = it->ptr;
it->end = &msg->bytes[len];
// Ok to pop this using VarInt because it's supposed to be 3, which is less than 253
uint64_t type = 0;
if (VarInt_pop(it, &type)) { return 1; }
if (type != Announce_Type_LINK_STATE) { return 1; }
// Should be < 8
uint64_t padCount = 0;
if (VarInt_pop(it, &padCount)) { return 1; }
for (unsigned int i = 0; i < padCount; i++) {
if (VarInt_pop(it, NULL)) { return 1; }
}
return 0;
}
static inline int LinkState_getNodeId(const struct VarInt_Iter* iter, uint32_t* nodeId)
{
struct VarInt_Iter it;
VarInt_clone(&it, iter);
uint64_t id;
if (VarInt_pop(&it, &id)) { return 1; }
*nodeId = id;
return 0;
}
#define LinkState_POP(it) (__extension__ ({ \
uint64_t x; \
if (VarInt_pop((it), &x)) { return 1; } \
x; \
}))
static inline int LinkState_decode(const struct VarInt_Iter* iter, struct LinkState* ls)
{
struct VarInt_Iter it;
VarInt_clone(&it, iter);
ls->nodeId = LinkState_POP(&it);
uint32_t i = LinkState_POP(&it);
uint32_t count = 0;
if (i >= LinkState_SLOTS) { return 1; }
for (;;) {
if (it.ptr == it.end) { break; }
ls->lagSlots[i] = LinkState_POP(&it);
ls->dropSlots[i] = LinkState_POP(&it);
ls->kbRecvSlots[i] = LinkState_POP(&it);
i = (i + 1) % LinkState_SLOTS;
count++;
}
ls->samples += count;
return 0;
}
#endif