litespeed-quic/src/liblsquic/lsquic_varint.c

203 lines
5.0 KiB
C

/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */
/*
* lsquic_varint.c -- routines dealing with IETF QUIC varint.
*/
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "lsquic_byteswap.h"
#include "lsquic_varint.h"
/* Returns number of bytes read from p (1, 2, 4, or 8), or a negative
* value on error.
*/
int
lsquic_varint_read (const unsigned char *p, const unsigned char *end,
uint64_t *valp)
{
uint64_t val;
if (p >= end)
return -1;
switch (*p >> 6)
{
case 0:
*valp = *p;
return 1;
case 1:
if (p + 1 >= end)
return -1;
*valp = (p[0] & VINT_MASK) << 8
| p[1]
;
return 2;
case 2:
if (p + 3 >= end)
return -1;
*valp = (p[0] & VINT_MASK) << 24
| p[1] << 16
| p[2] << 8
| p[3] << 0
;
return 4;
default:
if (p + 7 >= end)
return -1;
memcpy(&val, p, 8);
#if __BYTE_ORDER == __LITTLE_ENDIAN
val = bswap_64(val);
#endif
val &= (1ULL << 62) - 1;
*valp = val;
return 8;
}
}
int
lsquic_varint_read_nb (const unsigned char **pp, const unsigned char *end,
struct varint_read_state *state)
{
const unsigned char *p = *pp;
if (p >= end)
return -1;
switch (state->pos ? state->pos : *p >> 6)
{
case 0:
state->val = *p++;
*pp = p;
return 0;
case 1:
state->val = (*p++ & VINT_MASK) << 8;
if (p >= end) { state->pos = 1000; break; }
/* fall through */
case 1000:
state->val |= *p++;
*pp = p;
return 0;
case 2:
if (p + 3 < end)
{
state->val = (p[0] & VINT_MASK) << 24
| p[1] << 16
| p[2] << 8
| p[3] << 0
;
*pp += 4;
return 0;
}
state->val = (*p++ & VINT_MASK) << 24;
if (p >= end) { state->pos = 1001; break; }
/* fall through */
case 1001:
state->val |= *p++ << 16;
if (p >= end) { state->pos = 1002; break; }
/* fall through */
case 1002:
state->val |= *p++ << 8;
if (p >= end) { state->pos = 1003; break; }
/* fall through */
case 1003:
state->val |= *p++;
*pp = p;
return 0;
case 3:
if (p + 7 < end)
{
memcpy(&state->val, p, 8);
#if __BYTE_ORDER == __LITTLE_ENDIAN
state->val = bswap_64(state->val);
#endif
state->val &= (1ULL << 62) - 1;
*pp += 8;
return 0;
}
state->val = (uint64_t) (*p++ & VINT_MASK) << 56;
if (p >= end) { state->pos = 1004; break; }
/* fall through */
case 1004:
state->val |= (uint64_t) *p++ << 48;
if (p >= end) { state->pos = 1005; break; }
/* fall through */
case 1005:
state->val |= (uint64_t) *p++ << 40;
if (p >= end) { state->pos = 1006; break; }
/* fall through */
case 1006:
state->val |= (uint64_t) *p++ << 32;
if (p >= end) { state->pos = 1007; break; }
/* fall through */
case 1007:
state->val |= (uint64_t) *p++ << 24;
if (p >= end) { state->pos = 1008; break; }
/* fall through */
case 1008:
state->val |= (uint64_t) *p++ << 16;
if (p >= end) { state->pos = 1009; break; }
/* fall through */
case 1009:
state->val |= (uint64_t) *p++ << 8;
if (p >= end) { state->pos = 1010; break; }
/* fall through */
case 1010:
state->val |= *p++;
*pp = p;
return 0;
default:
assert(0);
}
*pp = p;
return -1;
}
int
lsquic_varint_read_two (const unsigned char **begin, const unsigned char *end,
struct varint_read2_state *state)
{
const unsigned char *p = *begin;
int s;
while (p < end)
{
switch (state->vr2s_state)
{
case VR2S_READ_ONE_BEGIN:
state->vr2s_varint_state.pos = 0;
state->vr2s_state = VR2S_READ_ONE_CONTINUE;
goto cont;
case VR2S_READ_TWO_BEGIN:
state->vr2s_varint_state.pos = 0;
state->vr2s_state = VR2S_READ_TWO_CONTINUE;
goto cont;
cont: case VR2S_READ_ONE_CONTINUE:
case VR2S_READ_TWO_CONTINUE:
s = lsquic_varint_read_nb(&p, end, &state->vr2s_varint_state);
if (s == 0)
{
if (state->vr2s_state == VR2S_READ_TWO_CONTINUE)
goto done;
state->vr2s_one = state->vr2s_varint_state.val;
state->vr2s_state = VR2S_READ_TWO_BEGIN;
break;
}
else
goto more;
}
}
more:
*begin = p;
return -1;
done:
*begin = p;
return 0;
}