175 lines
5.7 KiB
C
175 lines
5.7 KiB
C
|
#include "unpack.h"
|
||
|
#include "hevent.h"
|
||
|
#include "herr.h"
|
||
|
#include "hlog.h"
|
||
|
#include "hmath.h"
|
||
|
|
||
|
int hio_unpack(hio_t* io, void* buf, int readbytes) {
|
||
|
unpack_setting_t* setting = io->unpack_setting;
|
||
|
switch(setting->mode) {
|
||
|
case UNPACK_BY_FIXED_LENGTH:
|
||
|
return hio_unpack_by_fixed_length(io, buf, readbytes);
|
||
|
case UNPACK_BY_DELIMITER:
|
||
|
return hio_unpack_by_delimiter(io, buf, readbytes);
|
||
|
case UNPACK_BY_LENGTH_FIELD:
|
||
|
return hio_unpack_by_length_field(io, buf, readbytes);
|
||
|
default:
|
||
|
hio_read_cb(io, buf, readbytes);
|
||
|
return readbytes;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int hio_unpack_by_fixed_length(hio_t* io, void* buf, int readbytes) {
|
||
|
const unsigned char* sp = (const unsigned char*)io->readbuf.base + io->readbuf.head;
|
||
|
const unsigned char* ep = (const unsigned char*)buf + readbytes;
|
||
|
unpack_setting_t* setting = io->unpack_setting;
|
||
|
|
||
|
int fixed_length = setting->fixed_length;
|
||
|
assert(io->readbuf.len >= fixed_length);
|
||
|
|
||
|
const unsigned char* p = sp;
|
||
|
int remain = ep - p;
|
||
|
int handled = 0;
|
||
|
while (remain >= fixed_length) {
|
||
|
hio_read_cb(io, (void*)p, fixed_length);
|
||
|
handled += fixed_length;
|
||
|
p += fixed_length;
|
||
|
remain -= fixed_length;
|
||
|
}
|
||
|
|
||
|
io->readbuf.head = 0;
|
||
|
io->readbuf.tail = remain;
|
||
|
if (remain) {
|
||
|
// [p, p+remain] => [base, base+remain]
|
||
|
if (p != (unsigned char*)io->readbuf.base) {
|
||
|
memmove(io->readbuf.base, p, remain);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return handled;
|
||
|
}
|
||
|
|
||
|
int hio_unpack_by_delimiter(hio_t* io, void* buf, int readbytes) {
|
||
|
const unsigned char* sp = (const unsigned char*)io->readbuf.base + io->readbuf.head;
|
||
|
const unsigned char* ep = (const unsigned char*)buf + readbytes;
|
||
|
unpack_setting_t* setting = io->unpack_setting;
|
||
|
|
||
|
unsigned char* delimiter = setting->delimiter;
|
||
|
int delimiter_bytes = setting->delimiter_bytes;
|
||
|
|
||
|
const unsigned char* p = (const unsigned char*)buf - delimiter_bytes + 1;
|
||
|
if (p < sp) p = sp;
|
||
|
int remain = ep - p;
|
||
|
int handled = 0;
|
||
|
int i = 0;
|
||
|
while (remain >= delimiter_bytes) {
|
||
|
for (i = 0; i < delimiter_bytes; ++i) {
|
||
|
if (p[i] != delimiter[i]) {
|
||
|
goto not_match;
|
||
|
}
|
||
|
}
|
||
|
match:
|
||
|
p += delimiter_bytes;
|
||
|
remain -= delimiter_bytes;
|
||
|
hio_read_cb(io, (void*)sp, p - sp);
|
||
|
handled += p - sp;
|
||
|
sp = p;
|
||
|
continue;
|
||
|
not_match:
|
||
|
++p;
|
||
|
--remain;
|
||
|
}
|
||
|
|
||
|
remain = ep - sp;
|
||
|
io->readbuf.head = 0;
|
||
|
io->readbuf.tail = remain;
|
||
|
if (remain) {
|
||
|
// [sp, sp+remain] => [base, base+remain]
|
||
|
if (sp != (unsigned char*)io->readbuf.base) {
|
||
|
memmove(io->readbuf.base, sp, remain);
|
||
|
}
|
||
|
if (io->readbuf.tail == io->readbuf.len) {
|
||
|
if (io->readbuf.len >= setting->package_max_length) {
|
||
|
hloge("recv package over %d bytes!", (int)setting->package_max_length);
|
||
|
io->error = ERR_OVER_LIMIT;
|
||
|
hio_close(io);
|
||
|
return -1;
|
||
|
}
|
||
|
int newsize = MIN(io->readbuf.len * 2, setting->package_max_length);
|
||
|
hio_alloc_readbuf(io, newsize);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return handled;
|
||
|
}
|
||
|
|
||
|
int hio_unpack_by_length_field(hio_t* io, void* buf, int readbytes) {
|
||
|
const unsigned char* sp = (const unsigned char*)io->readbuf.base + io->readbuf.head;
|
||
|
const unsigned char* ep = (const unsigned char*)buf + readbytes;
|
||
|
unpack_setting_t* setting = io->unpack_setting;
|
||
|
|
||
|
const unsigned char* p = sp;
|
||
|
int remain = ep - p;
|
||
|
int handled = 0;
|
||
|
unsigned int head_len = setting->body_offset;
|
||
|
unsigned int body_len = 0;
|
||
|
unsigned int package_len = head_len;
|
||
|
const unsigned char* lp = NULL;
|
||
|
while (remain >= setting->body_offset) {
|
||
|
body_len = 0;
|
||
|
lp = p + setting->length_field_offset;
|
||
|
if (setting->length_field_coding == BIG_ENDIAN) {
|
||
|
for (int i = 0; i < setting->length_field_bytes; ++i) {
|
||
|
body_len = (body_len << 8) | (unsigned int)*lp++;
|
||
|
}
|
||
|
}
|
||
|
else if (setting->length_field_coding == LITTLE_ENDIAN) {
|
||
|
for (int i = 0; i < setting->length_field_bytes; ++i) {
|
||
|
body_len |= ((unsigned int)*lp++) << (i * 8);
|
||
|
}
|
||
|
}
|
||
|
else if (setting->length_field_coding == ENCODE_BY_VARINT) {
|
||
|
int varint_bytes = ep - lp;
|
||
|
body_len = varint_decode(lp, &varint_bytes);
|
||
|
if (varint_bytes == 0) break;
|
||
|
if (varint_bytes == -1) {
|
||
|
hloge("varint is too big!");
|
||
|
io->error = ERR_OVER_LIMIT;
|
||
|
hio_close(io);
|
||
|
return -1;
|
||
|
}
|
||
|
head_len = setting->body_offset + varint_bytes - setting->length_field_bytes;
|
||
|
}
|
||
|
package_len = head_len + body_len + setting->length_adjustment;
|
||
|
if (remain >= package_len) {
|
||
|
hio_read_cb(io, (void*)p, package_len);
|
||
|
handled += package_len;
|
||
|
p += package_len;
|
||
|
remain -= package_len;
|
||
|
} else {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
io->readbuf.head = 0;
|
||
|
io->readbuf.tail = remain;
|
||
|
if (remain) {
|
||
|
// [p, p+remain] => [base, base+remain]
|
||
|
if (p != (unsigned char*)io->readbuf.base) {
|
||
|
memmove(io->readbuf.base, p, remain);
|
||
|
}
|
||
|
if (package_len > io->readbuf.len) {
|
||
|
if (package_len > setting->package_max_length) {
|
||
|
hloge("package length over %d bytes!", (int)setting->package_max_length);
|
||
|
io->error = ERR_OVER_LIMIT;
|
||
|
hio_close(io);
|
||
|
return -1;
|
||
|
}
|
||
|
int newsize = LIMIT(package_len, io->readbuf.len * 2, setting->package_max_length);
|
||
|
hio_alloc_readbuf(io, newsize);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return handled;
|
||
|
}
|