#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; }