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