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