diff --git a/src/encode.c b/src/encode.c new file mode 100644 index 0000000000000000000000000000000000000000..3e630c10af82be077b665e8e4c44b6a9b8896af2 --- /dev/null +++ b/src/encode.c @@ -0,0 +1,518 @@ +/*\ +||| This file a part of Pike, and is copyright by Fredrik Hubinette +||| Pike is distributed as GPL (General Public License) +||| See the files COPYING and DISCLAIMER for more information. +\*/ +#include "stralloc.h" +#include "global.h" +#include "types.h" +#include "macros.h" +#include "object.h" +#include "constants.h" +#include "interpret.h" +#include "svalue.h" +#include "mapping.h" +#include "array.h" +#include "multiset.h" +#include "builtin_functions.h" +#include "dynamic_buffer.h" +#include "error.h" + +#ifdef _AIX +#include <net/nh.h> +#endif + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +struct encode_data +{ + struct svalue counter; + struct mapping *encoded; + dynamic_buffer buf; +}; + +#define addstr(s, l) low_my_binary_strcat((s), (l), &(data->buf)) +#define addchar(t) low_my_putchar((t),&(data->buf)) + +/* Current encoding: �ik0 */ +#define T_AGAIN 15 +#define T_MASK 15 +#define T_NEG 32 +#define T_SMALL 64 +#define SIZE_SHIFT 6 +#define MAX_SMALL (1<<(8-SIZE_SHIFT)) +#define COUNTER_START -MAX_SMALL + +/* Let's cram those bits... */ +static void code_entry(int type, INT32 num, struct encode_data *data) +{ + int t; + if(num<0) + { + type|=T_NEG; + num=~num; + } + + if(num < MAX_SMALL) + { + type|=T_SMALL | (num << SIZE_SHIFT); + addchar(type); + return; + }else{ + num-=MAX_SMALL; + } + + for(t=0;t<3;t++) + { + if(num >= (256 << (t<<3))) + num-=(256 << (t<<3)); + else + break; + } + + type|=t << SIZE_SHIFT; + addchar(type); + + switch(t) + { + case 3: addchar(num >> 24); + case 2: addchar(num >> 16); + case 1: addchar(num >> 8); + case 0: addchar(num); + } +} + +static void encode_value2(struct svalue *val, struct encode_data *data) +{ + INT32 i; + struct svalue *tmp; + if(tmp=low_mapping_lookup(data->encoded, val)) + { + code_entry(T_AGAIN, tmp->u.integer, data); + return; + }else{ + mapping_insert(data->encoded, val, &data->counter); + data->counter.u.integer++; + } + + switch(val->type) + { + case T_INT: + code_entry(T_INT, val->u.integer,data); + break; + + case T_STRING: + code_entry(T_STRING, val->u.string->len,data); + addstr(val->u.string->str, val->u.string->len); + break; + + case T_FLOAT: + { + if(val->u.float_number==0.0) + { + code_entry(T_FLOAT,0,data); + code_entry(T_FLOAT,0,data); + }else{ + INT32 x,y; + y=(int)ceil(log(val->u.float_number)/log(2.0))-31; + x=(int)((val->u.float_number)*pow(2.0,(float)-y)); + while(x && y && !(x&1)) + { + x>>=1; + y++; + } + code_entry(T_FLOAT,x,data); + code_entry(T_FLOAT,y,data); + } + } + + case T_ARRAY: + code_entry(T_ARRAY, val->u.array->size, data); + for(i=0; i<val->u.array->size; i++) + encode_value2(ITEM(val->u.array)+i, data); + break; + + case T_MAPPING: + check_stack(2); + val->u.mapping->refs++; + push_mapping(val->u.mapping); + f_indices(1); + + val->u.mapping->refs++; + push_mapping(val->u.mapping); + f_values(1); + + code_entry(T_MAPPING, sp[-2].u.array->size,data); + for(i=0; i<sp[-2].u.array->size; i++) + { + encode_value2(ITEM(sp[-2].u.array)+i, data); /* indices */ + encode_value2(ITEM(sp[-1].u.array)+i, data); /* values */ + } + pop_n_elems(2); + break; + + case T_MULTISET: + code_entry(T_MULTISET, val->u.multiset->ind->size,data); + for(i=0; i<val->u.multiset->ind->size; i++) + encode_value2(ITEM(val->u.multiset->ind)+i, data); + break; + + case T_OBJECT: + case T_FUNCTION: + case T_PROGRAM: + check_stack(1); + push_svalue(val); + APPLY_MASTER("nameof", 1); + if(sp[-1].type == val[-1].type) + error("Error in master()->nameof(), same type returned.\n"); + code_entry(val->type, 0,data); + encode_value2(sp-1, data); + pop_stack(); + break; + } +} + +static void free_encode_data(struct encode_data *data) +{ + toss_buffer(& data->buf); + free_mapping(data->encoded); +} + +void f_encode_value(INT32 args) +{ + ONERROR tmp; + struct encode_data d, *data; + data=&d; + + initialize_buf(&data->buf); + data->encoded=allocate_mapping(128); + data->counter.type=T_INT; + data->counter.u.integer=COUNTER_START; + + SET_ONERROR(tmp, free_encode_data, data); + addstr("\266ke0", 4); + encode_value2(sp-args, data); + UNSET_ONERROR(tmp); + + free_mapping(data->encoded); + pop_n_elems(args); + push_string(low_free_buf(&data->buf)); +} + +struct decode_data +{ + unsigned char *data; + INT32 len; + INT32 ptr; + struct mapping *decoded; + struct svalue counter; +}; + +static int my_extract_char(struct decode_data *data) +{ + if(data->len >= data->ptr) + error("Format error, not enough data in string.\n"); + return data->data [ data->ptr++ ]; +} + +#define GETC() my_extract_char(data) + +#define DECODE() \ + what=GETC(); \ + e=what>>SIZE_SHIFT; \ + if(what & T_SMALL) { \ + num=e; \ + } else { \ + num=0; \ + while(e-->=0) num=(num<<8) + (GETC()+1); \ + num+=MAX_SMALL - 1; \ + } \ + if(what & T_NEG) num=~num + +static void decode_value2(struct decode_data *data) +{ + INT32 what, e, num; + struct svalue tmp, *tmp2; + + DECODE(); + + check_stack(1); + + switch(what & T_MASK) + { + case T_AGAIN: + tmp.type=T_INT; + tmp.subtype=0; + tmp.u.integer=num; + if(tmp2=low_mapping_lookup(data->decoded, &tmp)) + { + push_svalue(tmp2); + }else{ + error("Failed to decode string. (invalid T_AGAIN)\n"); + } + return; + + case T_INT: + tmp=data->counter; + data->counter.u.integer++; + push_int(num); + break; + + case T_STRING: + tmp=data->counter; + data->counter.u.integer++; + if(data->ptr + num >= data->len) + error("Failed to decode string. (string range error)\n"); + push_string(make_shared_binary_string(data->data + data->ptr, num)); + data->ptr+=num; + break; + + case T_FLOAT: + { + INT32 num2=num; + + tmp=data->counter; + data->counter.u.integer++; + + DECODE(); + push_float(num2 * pow(2.0, (double) num)); + break; + } + case T_ARRAY: + { + struct array *a=allocate_array(num); + tmp.type=T_ARRAY; + tmp.u.array=a; + mapping_insert(data->decoded, & data->counter, &tmp); + data->counter.u.integer++; + + /* Since a reference to the array is stored in the mapping, we can + * safely decrease this reference here. Thus it will be automatically + * freed if something goes wrong. + */ + a->refs--; + + for(e=0;e<num;e++) + { + decode_value2(data); + ITEM(a)[e]=sp[-1]; + sp--; + } + a->refs++; + push_array(a); + return; + } + + case T_MAPPING: + { + struct mapping *m=allocate_mapping(num); + tmp.type=T_MAPPING; + tmp.u.mapping=m; + mapping_insert(data->decoded, & data->counter, &tmp); + data->counter.u.integer++; + m->refs--; + + for(e=0;e<num;e++) + { + decode_value2(data); + decode_value2(data); + mapping_insert(m, sp-2, sp-1); + pop_n_elems(2); + } + m->refs++; + push_mapping(m); + return; + } + + case T_MULTISET: + { + struct multiset *m=mkmultiset(low_allocate_array(0, num)); + tmp.type=T_MULTISET; + tmp.u.multiset=m; + mapping_insert(data->decoded, & data->counter, &tmp); + data->counter.u.integer++; + m->refs--; + + for(e=0;e<num;e++) + { + decode_value2(data); + multiset_insert(m, sp-1); + pop_stack(); + } + m->refs++; + push_multiset(m); + return; + } + + case T_OBJECT: + case T_FUNCTION: + case T_PROGRAM: + tmp=data->counter; + data->counter.u.integer++; + decode_value2(data); + switch(what & T_MASK) + { + case T_OBJECT: APPLY_MASTER("objectof", 1); break; + case T_FUNCTION: APPLY_MASTER("functionof", 1); break; + case T_PROGRAM: APPLY_MASTER("programof", 1); break; + } + if(sp[-1].type != (what & T_MASK)) + error("Failed to restore string. (master() failed)\n"); + break; + + default: + error("Failed to restore string. (Illegal type)\n"); + } + + mapping_insert(data->decoded, & tmp, sp-1); +} + + +static void free_decode_data(struct decode_data *data) +{ + free_mapping(data->decoded); +} + +static INT32 my_decode(struct pike_string *tmp) +{ + ONERROR err; + struct decode_data d, *data; + data=&d; + data->counter.type=T_INT; + data->counter.u.integer=COUNTER_START; + data->data=tmp->str; + data->len=tmp->len; + data->ptr=0; + + + if(GETC() != 182 || + GETC() != 'k' || + GETC() != 'e' || + GETC() != '0') + return 0; + + data->decoded=allocate_mapping(128); + + SET_ONERROR(err, free_decode_data, data); + decode_value2(data); + UNSET_ONERROR(err); + free_mapping(data->decoded); + return 1; +} + +/* Compatibilidy decoder */ + +static unsigned char extract_char(char **v, INT32 *l) +{ + if(!*l) error("Format error, not enough place for char.\n"); + else (*l)--; + (*v)++; + return ((unsigned char *)(*v))[-1]; +} + +static INT32 extract_int(char **v, INT32 *l) +{ + INT32 j,i; + + j=extract_char(v,l); + if(j & 0x80) return (j & 0x7f); + + if((j & ~8) > 4) + error("Format Error: Error in format string, invalid integer.\n"); + i=0; + while(j & 7) { i=(i<<8) | extract_char(v,l); j--; } + if(j & 8) return -i; + return i; +} + +static void rec_restore_value(char **v, INT32 *l) +{ + INT32 t,i; + + i=extract_int(v,l); + t=extract_int(v,l); + switch(i) + { + case T_INT: push_int(t); return; + + case T_FLOAT: + if(sizeof(INT32) < sizeof(float)) /* FIXME FIXME FIXME FIXME */ + error("Float architecture not supported.\n"); + push_int(t); /* WARNING! */ + sp[-1].type = T_FLOAT; + return; + + case T_STRING: + if(*l < t) error("Format error, string to short\n"); + push_string(make_shared_binary_string(*v, t)); + (*l)-= t; (*v)+= t; + return; + + case T_ARRAY: + check_stack(t); + for(i=0;i<t;i++) rec_restore_value(v,l); + f_aggregate(t); + return; + + case T_MULTISET: + check_stack(t); + for(i=0;i<t;i++) rec_restore_value(v,l); + f_aggregate_multiset(t); + return; + + case T_MAPPING: + check_stack(t*2); + for(i=0;i<t;i++) + { + rec_restore_value(v,l); + rec_restore_value(v,l); + } + f_aggregate_mapping(t*2); + return; + + case T_OBJECT: + if(*l < t) error("Format error, string to short\n"); + push_string(make_shared_binary_string(*v, t)); + (*l) -= t; (*v) += t; + APPLY_MASTER("objectof", 1); + return; + + case T_FUNCTION: + if(*l < t) error("Format error, string to short\n"); + push_string(make_shared_binary_string(*v, t)); + (*l) -= t; (*v) += t; + APPLY_MASTER("functionof", 1); + return; + + case T_PROGRAM: + if(*l < t) error("Format error, string to short\n"); + push_string(make_shared_binary_string(*v, t)); + (*l) -= t; (*v) += t; + APPLY_MASTER("programof", 1); + return; + + default: + error("Format error. Unknown type\n"); + } +} + +void f_decode_value(INT32 args) +{ + struct pike_string *s; + + if(args != 1 || (sp[-1].type != T_STRING)) + error("Illegal argument to restore_value(STRING)\n"); + + s = sp[-1].u.string; + if(!my_decode(s)) + { + char *v=s->str; + INT32 l=s->len; + rec_restore_value(&v, &l); + } + assign_svalue(sp-args-1, sp-1); + pop_n_elems(args); +} + diff --git a/src/encode.h b/src/encode.h new file mode 100644 index 0000000000000000000000000000000000000000..61286af7c05ac5b1b5fd0da40380521197d4a1a9 --- /dev/null +++ b/src/encode.h @@ -0,0 +1,16 @@ +/*\ +||| This file a part of Pike, and is copyright by Fredrik Hubinette +||| Pike is distributed as GPL (General Public License) +||| See the files COPYING and DISCLAIMER for more information. +\*/ +#ifndef ENCODE_H +#define ENCODE_H + +/* Prototypes begin here */ +struct encode_data; +void f_encode_value(INT32 args); +struct decode_data; +void f_decode_value(INT32 args); +/* Prototypes end here */ + +#endif