aboutsummaryrefslogblamecommitdiffstats
path: root/lib/erl_interface/src/legacy/erl_marshal.c
blob: c18067b9bc84f0d38d7b8e227608a4b941e7bfa0 (plain) (tree)
1
2
3
4
5
6
7
8
9

                   
  
                                                        
  


                                                                   
  






                                                                           
  











                                               
                   
















                                                


                                                                                                                  






                                                                    






                                                                        
















                                                                     
                                                 
                                                   

























                                                                                    
                                           






                                                    
                                                    
                                                    
                                                      

                                                   
                                                   


                                                    
                                                    
                                                   
                                                   





                                                     






                                                 
                                                 




























                                                                




                                                                   
                                                          
     
          





                                                                 













                                                           
                                           


                     
                                





                                              
                                                  









                                     








                                                         

                           
                      



                                              
                                                  








                                     



                                                                     
                       
                                                          

                                         




                                          



                                    


                                              
         
                                                  









                                     
                                                  













                                                           
                       



                                              
                                                  






                                      

                                       
                                               
                                                










                                                 











                                        
                 
     
                   

                                           





                                                           




                                                 

                                                    











                                                









                                                   

                                       
                                           
                                                 





                                     











                                        
                 
     













































































































































                                                                      

                                            

                                             

 










                                                   
                                              



                          
                                          




                           
                              





                                 

                                               





                                               
                                                        




                                  
                                                                   


                 
                                                                                     


                  
                                                                



























































































                                                                                                  
 
                                                           
 
                              
                 
                             
                                                                         
                                                                       
                 









                                      
                                                   


                                    
                                                 



                            

               









                                                
                  
              
                      



                                                  
 

                    






















                                                                                         






                                            



                                   









                                                                               
               


                                                             

















                                                                                     



                                            
 


                                                                                    
 










                                              
         
                    










                                                                     
                            


                                 
                                
                 
                                                              


                     
                         
         
                                        
                                  
 

                                                                       

                                  





                                                             






                                                                  

                                                            

                           
         
                                          
                                   
 

                                                                       

                                  
                                                           

                                                
                                   

                                                         

         

                                 
         

                              
                                  
 
                                   

                                               
 
                                                                       

                                  






                                                                  

                                    


                                                               
             

                                                           


                      
                          
         
                                
                                  
 

                                                                        

                                  


                                                             






                                                                   

                                                     














































































                                                                                   
                       
                                 


                                                
                         
                  



















































































































































                                                                                
                           
                            
                                 

                        
                         

                       
                          


                               
                                 








                              
                       

































                                                   
                           
                            
                                 
                     
                         
                      
                          

                               
                                 



                        
                       




























                                                             







                                         
                           




                                 
                                 










                       









                                             
                              
    
                  








                               
                           
                            
                                 
                       

                     
                                      
                          
              



                                      

                           
                                      
                      
              



                                      
                               
                                 


                                     
                                      
                                                             


































                                                                            


                       











































































































                                                                              




                                                                                               

                                              












































                                                                              











                                                                 
                                                                 
                                                                 





















































                                                                            
                                   






































                                                                            
                                    
























                                                           
                           






                                                                            

                  
            
                


















                                                                            
                           



                               
                                 

                           
                                                 

               
                 












                                                    

                                     

                                                    

                            

                                              

                                   


                                       

                                          


                                                        


                      

















                                                        
                                  


                                                            
                                     




                                                









                                                                      







                                                    





























                                                                                








                                                        

































































































































































































                                                                      




                                                    
























































































































































































































                                                                            
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 1996-2018. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */
/*
 * Purpose: Decoding and encoding Erlang terms.
 */  
#include "eidef.h"

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <string.h>
#include <limits.h>

#include "erl_interface.h"
#include "erl_marshal.h"
#include "erl_eterm.h"
#include "erl_malloc.h"
#include "erl_error.h"
#include "erl_internal.h"

#include "eiext.h" /* replaces external.h */
#include "putget.h"

static int is_string(ETERM* term);
#if defined(VXWORKS) && CPU == PPC860
int erl_fp_compare(unsigned *a, unsigned *b);
static void erl_long_to_fp(long l, unsigned *d);
#endif

static int cmpbytes(unsigned char* s1,int l1,unsigned char* s2,int l2);
static int cmpatoms(unsigned char* s1, int l1, unsigned char tag1, unsigned char* s2, int l2, unsigned char tag2);

/* Used when comparing two encoded byte arrays */
/* this global data is ok (from threading point of view) since it is
 * initialized once and never changed
 */

#define CMP_ARRAY_SIZE 256
/* FIXME problem for threaded ? */

static enum
{
    ERL_NUM_CMP=1, ERL_ATOM_CMP, ERL_REF_CMP, ERL_FUN_CMP, ERL_PORT_CMP,
    ERL_PID_CMP, ERL_TUPLE_CMP, ERL_NIL_CMP, ERL_LIST_CMP, ERL_BIN_CMP
}cmp_array[CMP_ARRAY_SIZE];

static int init_cmp_array_p=1; /* initialize array, the first time */

#if defined(VXWORKS) && CPU == PPC860
#include <limits.h>
#endif

#if defined(__GNUC__)
#  define INLINE __inline__
#elif defined(__WIN32__)
#  define INLINE __inline
#else
#  define INLINE
#endif

static int cmp_floats(double f1, double f2);
static INLINE double to_float(long l);

#define IS_ERL_NUM(t) (cmp_array[t]==ERL_NUM_CMP)
#define IS_ERL_ATOM(t) (cmp_array[t]==ERL_ATOM_CMP)

#define CMP_NUM_CLASS_SIZE 256
static unsigned char cmp_num_class[CMP_NUM_CLASS_SIZE]; 
static int init_cmp_num_class_p=1; /* initialize array, the first time */

#define MK_CMP_NUM_CODE(x,y)    (((x)<<2)|(y))
#define CMP_NUM_CLASS(x)        (cmp_num_class[x] & 0x03)
#define CMP_NUM_CODE(x,y)       (MK_CMP_NUM_CODE(CMP_NUM_CLASS(x),CMP_NUM_CLASS(y)))

#define SMALL 1
#define FLOAT 2
#define BIG   3

#define SMALL_SMALL    MK_CMP_NUM_CODE(SMALL,SMALL)
#define SMALL_FLOAT    MK_CMP_NUM_CODE(SMALL,FLOAT)
#define SMALL_BIG      MK_CMP_NUM_CODE(SMALL,BIG)
#define FLOAT_SMALL    MK_CMP_NUM_CODE(FLOAT,SMALL)
#define FLOAT_FLOAT    MK_CMP_NUM_CODE(FLOAT,FLOAT)
#define FLOAT_BIG      MK_CMP_NUM_CODE(FLOAT,BIG)
#define BIG_SMALL      MK_CMP_NUM_CODE(BIG,SMALL)
#define BIG_FLOAT      MK_CMP_NUM_CODE(BIG,FLOAT)
#define BIG_BIG        MK_CMP_NUM_CODE(BIG,BIG)

void erl_init_marshal(void)
{
  if (init_cmp_array_p) {
    memset(cmp_array, 0, sizeof cmp_array);
    cmp_array[ERL_SMALL_INTEGER_EXT] = ERL_NUM_CMP;
    cmp_array[ERL_INTEGER_EXT]       = ERL_NUM_CMP;
    cmp_array[ERL_FLOAT_EXT]         = ERL_NUM_CMP;
    cmp_array[NEW_FLOAT_EXT]         = ERL_NUM_CMP;
    cmp_array[ERL_SMALL_BIG_EXT]     = ERL_NUM_CMP;
    cmp_array[ERL_LARGE_BIG_EXT]     = ERL_NUM_CMP;
    cmp_array[ERL_ATOM_EXT]          = ERL_ATOM_CMP;
    cmp_array[ERL_ATOM_UTF8_EXT]     = ERL_ATOM_CMP;
    cmp_array[ERL_SMALL_ATOM_EXT]    = ERL_ATOM_CMP;
    cmp_array[ERL_SMALL_ATOM_UTF8_EXT] = ERL_ATOM_CMP;
    cmp_array[ERL_REFERENCE_EXT]     = ERL_REF_CMP;
    cmp_array[ERL_NEW_REFERENCE_EXT] = ERL_REF_CMP;
    cmp_array[ERL_NEWER_REFERENCE_EXT]=ERL_REF_CMP;
    cmp_array[ERL_FUN_EXT]           = ERL_FUN_CMP;
    cmp_array[ERL_NEW_FUN_EXT]       = ERL_FUN_CMP;
    cmp_array[ERL_PORT_EXT]          = ERL_PORT_CMP;
    cmp_array[ERL_NEW_PORT_EXT]      = ERL_PORT_CMP;
    cmp_array[ERL_PID_EXT]           = ERL_PID_CMP;
    cmp_array[ERL_NEW_PID_EXT]       = ERL_PID_CMP;
    cmp_array[ERL_SMALL_TUPLE_EXT]   = ERL_TUPLE_CMP;
    cmp_array[ERL_LARGE_TUPLE_EXT]   = ERL_TUPLE_CMP;
    cmp_array[ERL_NIL_EXT]           = ERL_NIL_CMP;
    cmp_array[ERL_STRING_EXT]        = ERL_LIST_CMP;
    cmp_array[ERL_LIST_EXT]          = ERL_LIST_CMP;
    cmp_array[ERL_BINARY_EXT]        = ERL_BIN_CMP;
    init_cmp_array_p = 0;
  }
  if (init_cmp_num_class_p) {
    memset(cmp_num_class, 0, CMP_NUM_CLASS_SIZE);
    cmp_num_class[ERL_SMALL_INTEGER_EXT] = SMALL;
    cmp_num_class[ERL_INTEGER_EXT]       = SMALL;
    cmp_num_class[ERL_FLOAT_EXT]         = FLOAT;
    cmp_num_class[NEW_FLOAT_EXT]         = FLOAT;
    cmp_num_class[ERL_SMALL_BIG_EXT]     = BIG;
    cmp_num_class[ERL_LARGE_BIG_EXT]     = BIG;
    init_cmp_num_class_p = 0;
  }
}

/* The encoder calls length, if erl_length() should return */
/* -1 for dotted pairs (why !!!!) we can't use erl_length() */
/* from the encoder in erl_marshal.c */
 
static int erl_length_x(const ETERM *ep) {
    int n = 0;

    if (!ep) return -1;

    while (ERL_TYPE(ep) == ERL_LIST) {
       n++;
       ep = TAIL(ep);
    }

    return n;
}


/*==============================================================
 * Marshalling routines.
 *==============================================================
 */

static void encode_atom(Erl_Atom_data* a, unsigned char **ext)
{
    int ix = 0;
    if (a->latin1) {
	ei_encode_atom_len_as((char*)*ext, &ix, a->latin1, a->lenL,
			      ERLANG_LATIN1, ERLANG_UTF8);
    }
    else {
	ei_encode_atom_len_as((char*)*ext, &ix, a->utf8, a->lenU,
			      ERLANG_UTF8, ERLANG_UTF8);
    }
    *ext += ix;
}

/* 
 * The actual ENCODE engine.
 * Returns 0 on success, otherwise 1.
 */
int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
{
    int i;
    unsigned int u;
    long long l;
    unsigned long long ul;
    
    switch(ERL_TYPE(ep)) 
    {
    case ERL_ATOM:
	encode_atom(&ep->uval.aval.d, ext);
	return 0;

    case ERL_INTEGER:
	i = ep->uval.ival.i;	
	/* SMALL_INTEGER */
	if ((i < 256) && (i >= 0)) {
	    *(*ext)++ = ERL_SMALL_INTEGER_EXT;
	    *(*ext)++ = i & 0xff;
	    return 0;
	}
	/* R14B: Use all 32 bits of INTEGER_EXT */
	*(*ext)++ = ERL_INTEGER_EXT;
	*(*ext)++ = (i >> 24) & 0xff;
	*(*ext)++ = (i >> 16) & 0xff;
	*(*ext)++ = (i >> 8) & 0xff;
	*(*ext)++ = i  & 0xff;
	return 0;

    case ERL_U_INTEGER:
	u = ep->uval.uival.u;
	/* ERL_U_SMALL_BIG */
	if ((int)u < 0) {
	    *(*ext)++ = ERL_SMALL_BIG_EXT;
	    *(*ext)++ = 4;		/* four bytes */
	    *(*ext)++ = 0;		/* sign byte  */ 
	    *(*ext)++ = u  & 0xff;	/* LSB first  */
	    *(*ext)++ = (u >> 8) & 0xff;
	    *(*ext)++ = (u >> 16) & 0xff;
	    *(*ext)++ = (u >> 24) & 0xff; 
	    return 0;
	}
	/* SMALL_INTEGER */
	if (u < 256) {
	    *(*ext)++ = ERL_SMALL_INTEGER_EXT;
	    *(*ext)++ = u & 0xff;
	    return 0;
	}
	/* R14B: Use all 32 bits of INTEGER_EXT */
	*(*ext)++ = ERL_INTEGER_EXT;
	*(*ext)++ = (u >> 24) & 0xff;
	*(*ext)++ = (u >> 16) & 0xff;
	*(*ext)++ = (u >> 8) & 0xff;
	*(*ext)++ = u  & 0xff;
	return 0;
    case ERL_LONGLONG:
	l = ep->uval.llval.i;
	/* ERL_SMALL_BIG */
	if (l > ((long long) INT_MAX) || l < ((long long) INT_MIN)) {
	    *(*ext)++ = ERL_SMALL_BIG_EXT;
	    *(*ext)++ = 8;
	    if ((*(*ext)++ = (l<0))) /* sign byte  */ 
	    	l = -l;
	    *(*ext)++ =  l        & 0xff; /* LSB first  */
	    *(*ext)++ = (l >>  8) & 0xff;
	    *(*ext)++ = (l >> 16) & 0xff;
	    *(*ext)++ = (l >> 24) & 0xff; 
	    *(*ext)++ = (l >> 32) & 0xff; 
	    *(*ext)++ = (l >> 40) & 0xff; 
	    *(*ext)++ = (l >> 48) & 0xff; 
	    *(*ext)++ = (l >> 56) & 0xff; 
	    return 0;
	} 
	/* SMALL_INTEGER */
	if ((l < 256) && (l >= 0)) {
	    *(*ext)++ = ERL_SMALL_INTEGER_EXT;
	    *(*ext)++ = l & 0xff;
	    return 0;
	}
	/* R14B: Use all 32 bits of INTEGER_EXT */
	*(*ext)++ = ERL_INTEGER_EXT;
	*(*ext)++ = (l >> 24) & 0xff;
	*(*ext)++ = (l >> 16) & 0xff;
	*(*ext)++ = (l >>  8) & 0xff;
	*(*ext)++ = l  & 0xff;
	return 0;

    case ERL_U_LONGLONG:
	ul = ep->uval.ullval.u;
	/* ERL_U_SMALL_BIG */
	if (ul > ((unsigned long long) INT_MAX)) {
	    *(*ext)++ = ERL_SMALL_BIG_EXT;
	    *(*ext)++ = 8; /* eight bytes */
	    *(*ext)++ = 0; /* sign byte  */ 
	    *(*ext)++ =  ul        & 0xff; /* LSB first  */
	    *(*ext)++ = (ul >>  8) & 0xff;
	    *(*ext)++ = (ul >> 16) & 0xff;
	    *(*ext)++ = (ul >> 24) & 0xff; 
	    *(*ext)++ = (ul >> 32) & 0xff; 
	    *(*ext)++ = (ul >> 40) & 0xff; 
	    *(*ext)++ = (ul >> 48) & 0xff; 
	    *(*ext)++ = (ul >> 56) & 0xff; 
	    return 0;
	}
	/* SMALL_INTEGER */
	if (ul < 256) {
	    *(*ext)++ = ERL_SMALL_INTEGER_EXT;
	    *(*ext)++ = ul & 0xff;
	    return 0;
	}
	/* R14B: Use all 32 bits of INTEGER_EXT */
	*(*ext)++ = ERL_INTEGER_EXT;
	*(*ext)++ = (ul >> 24) & 0xff;
	*(*ext)++ = (ul >> 16) & 0xff;
	*(*ext)++ = (ul >>  8) & 0xff;
	*(*ext)++ =  ul        & 0xff;
	return 0;

    case ERL_PID: {
        unsigned char* tagp = (*ext)++;
	/* First poke in node as an atom */    
	encode_atom(&ep->uval.pidval.node, ext);
	/* And then fill in the integer fields */
	i = ERL_PID_NUMBER(ep);
	*(*ext)++ = (i >> 24) &0xff;
	*(*ext)++ = (i >> 16) &0xff;
	*(*ext)++ = (i >>  8) &0xff;
	*(*ext)++ = i &0xff;
	i = ERL_PID_SERIAL(ep);
	*(*ext)++ = (i >> 24) &0xff;
	*(*ext)++ = (i >> 16) &0xff;
	*(*ext)++ = (i >>  8) &0xff;
	*(*ext)++ = i &0xff;

        i = ERL_PID_CREATION(ep);
        if ((unsigned int)i <= 3) {
            *tagp = ERL_PID_EXT;
            *(*ext)++ = i;
        } else {
            *tagp = ERL_NEW_PID_EXT;
            *(*ext)++ = (i >> 24) &0xff;
            *(*ext)++ = (i >> 16) &0xff;
            *(*ext)++ = (i >>  8) &0xff;
            *(*ext)++ = i &0xff;
        }
	return 0;
    }
    case ERL_REF: {
            unsigned char* tagp = (*ext)++;

	    int len, j;

	    /* Always encode as an extended reference; all
	       participating parties are now expected to be
	       able to decode extended references. */

	    i = strlen((char *)ERL_REF_NODE(ep));
	    len = ERL_REF_LEN(ep);
	    *(*ext)++ = (len >> 8) &0xff;
	    *(*ext)++ = len &0xff;

	    encode_atom(&ep->uval.refval.node, ext);

            i = ERL_REF_CREATION(ep);
            if ((unsigned int)i <= 3) {
                *tagp = ERL_NEW_REFERENCE_EXT;
                *(*ext)++ = i;
            } else {
                *tagp = ERL_NEWER_REFERENCE_EXT;
                *(*ext)++ = (i >> 24) &0xff;
                *(*ext)++ = (i >> 16) &0xff;
                *(*ext)++ = (i >>  8) &0xff;
                *(*ext)++ = i &0xff;
            }

	    /* Then the integer fields */
	    for (j = 0; j < ERL_REF_LEN(ep); j++) {
		i = ERL_REF_NUMBERS(ep)[j];
		*(*ext)++ = (i >> 24) &0xff;
		*(*ext)++ = (i >> 16) &0xff;
		*(*ext)++ = (i >>  8) &0xff;
		*(*ext)++ = i &0xff;
	    }
	}
	return 0;
    case ERL_PORT: {
	unsigned char* tagp = (*ext)++;
	/* First poke in node as an atom */
	encode_atom(&ep->uval.portval.node, ext);
	/* Then the integer fields */
	i = ERL_PORT_NUMBER(ep);
	*(*ext)++ = (i >> 24) &0xff;
	*(*ext)++ = (i >> 16) &0xff;
	*(*ext)++ = (i >>  8) &0xff;
	*(*ext)++ = i &0xff;

        i = ERL_PORT_CREATION(ep);
        if ((unsigned int)i <= 3) {
            *tagp = ERL_PORT_EXT;
            *(*ext)++ = i;
        } else {
            *tagp = ERL_NEW_PORT_EXT;
            *(*ext)++ = (i >> 24) &0xff;
            *(*ext)++ = (i >> 16) &0xff;
            *(*ext)++ = (i >>  8) &0xff;
            *(*ext)++ = i &0xff;
        }
	return 0;
    }
    case ERL_EMPTY_LIST:
	*(*ext)++ = ERL_NIL_EXT;
	break;
    case ERL_LIST:
	i = is_string(ep);
	if (0 < i && i < 0x10000) { /* String. */
	    *(*ext)++ = ERL_STRING_EXT;
	    *(*ext)++ = (i >>8) &0xff;
	    *(*ext)++ = i &0xff;
	    while (ERL_TYPE(ep) == ERL_LIST) {
		*(*ext)++ = HEAD(ep)->uval.ival.i;
		ep = TAIL(ep);
	    }
	    break;
	} else {		/* List. */
	    i = erl_length_x(ep);
	    *(*ext)++ = ERL_LIST_EXT;
	    *(*ext)++ = (i >> 24) &0xff;
	    *(*ext)++ = (i >> 16) &0xff;
	    *(*ext)++ = (i >>  8) &0xff;
	    *(*ext)++ = i &0xff;
	    while (ERL_TYPE(ep) == ERL_LIST) {
		if (erl_encode_it(HEAD(ep), ext, dist))
		    return 1;
		ep = TAIL(ep);
	    }
	    i = erl_encode_it(ep, ext, dist);
	    return i;
	}
    case ERL_TUPLE:
	i = ep->uval.tval.size;
	if (i <= 0xff) {
	    *(*ext)++ = ERL_SMALL_TUPLE_EXT;
	    *(*ext)++ = i & 0xff;
	}
	else {
	    *(*ext)++ = ERL_LARGE_TUPLE_EXT;
	    *(*ext)++ = (i >> 24) & 0xff;
	    *(*ext)++ = (i >> 16) & 0xff;
	    *(*ext)++ = (i >>  8) & 0xff;
	    *(*ext)++ = i & 0xff;
	}
	for (i=0; i<ep->uval.tval.size; i++)
	    if (erl_encode_it(ep->uval.tval.elems[i], ext, dist))
		return 1;
	break;
    case ERL_FLOAT:
	*(*ext)++ = ERL_FLOAT_EXT;
	memset(*ext, 0, 31);
	sprintf((char *) *ext, "%.20e", ep->uval.fval.f);
	*ext += 31;
	break;
    case ERL_BINARY:
	*(*ext)++ = ERL_BINARY_EXT;
	i = ep->uval.bval.size;
	*(*ext)++ = (i >> 24) & 0xff;
	*(*ext)++ = (i >> 16) & 0xff;
	*(*ext)++ = (i >>  8) & 0xff;
	*(*ext)++ = i  & 0xff;
	memcpy((char *) *ext, (char*) ep->uval.bval.b, i);
	*ext += i;
	break;
    case ERL_FUNCTION:
	if (ERL_FUN_ARITY(ep) != -1) {
	    unsigned char *size_p = *ext + 1;
	    *(*ext)++ = ERL_NEW_FUN_EXT;
	    *ext += 4;
	    i = ERL_FUN_ARITY(ep);
	    put8(*ext, i);
	    memcpy(*ext, ERL_FUN_MD5(ep), 16);
	    *ext += 16;
	    i = ERL_FUN_NEW_INDEX(ep);
	    put32be(*ext, i);
	    i = ERL_CLOSURE_SIZE(ep);
	    put32be(*ext, i);
	    erl_encode_it(ERL_FUN_MODULE(ep), ext, dist);
	    erl_encode_it(ERL_FUN_INDEX(ep), ext, dist);
	    erl_encode_it(ERL_FUN_UNIQ(ep), ext, dist);
	    erl_encode_it(ERL_FUN_CREATOR(ep), ext, dist);
	    for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
		erl_encode_it(ep->uval.funcval.closure[i], ext, dist);
	    if (size_p != NULL) {
		i = *ext - size_p;
		put32be(size_p, i);
	    }
	} else {
	    *(*ext)++ = ERL_FUN_EXT;
	    i = ERL_CLOSURE_SIZE(ep);
	    *(*ext)++ = (i >> 24) & 0xff;
	    *(*ext)++ = (i >> 16) & 0xff;
	    *(*ext)++ = (i >>  8) & 0xff;
	    *(*ext)++ = i  & 0xff;
	    erl_encode_it(ERL_FUN_CREATOR(ep), ext, dist);
	    erl_encode_it(ERL_FUN_MODULE(ep), ext, dist);
	    erl_encode_it(ERL_FUN_INDEX(ep), ext, dist);
	    erl_encode_it(ERL_FUN_UNIQ(ep), ext, dist);
	    for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
		erl_encode_it(ep->uval.funcval.closure[i], ext, dist);
	}
	break;
    default:
	return 1;
    }
    return 0;
}

/* 
 * ENCODE an ETERM into a BUFFER, assuming BUFFER is of 
 * enough size. At success return number of bytes written 
 * into it, otherwise return 0.
 */
static int erl_encode3(ETERM *ep, unsigned char *t, int dist)
{
  unsigned char *x = t;
  
  *x++ = ERL_VERSION_MAGIC;
  if (erl_encode_it(ep, &x, dist)) {
#ifdef DEBUG
    erl_err_msg("<ERROR> erl_encode: Error while encoding");
#endif
    return 0;
  }
  return (x - t);

}

/* API */

int erl_encode(ETERM *ep, unsigned char *t)
{
    return erl_encode3(ep, t, 4);
}

/* determine the buffer size that will be required for the eterm */
static int erl_term_len_helper(ETERM *ep, int dist);

/* FIXME hard coded dist version */
int erl_term_len(ETERM *ep)
{
  return 1+erl_term_len_helper(ep, 4);
}

static int atom_len_helper(Erl_Atom_data* a)
{
    (void) erl_atom_ptr_utf8(a);
    return 1 + 1 + (a->lenU > 255) + a->lenU;
}

static int erl_term_len_helper(ETERM *ep, int dist)
{
  int len = 0;
  int i;
  unsigned int u;
  long long l;
  unsigned long long ul;

  if (ep) {
    switch (ERL_TYPE(ep)) {
    case ERL_ATOM:
      len = atom_len_helper(&ep->uval.aval.d);
      break;

    case ERL_INTEGER:
      i = ep->uval.ival.i;
      if ((i < 256) && (i >= 0)) len = 2; 
      else len = 5;
      break;

    case ERL_U_INTEGER:
      u = ep->uval.uival.u;
      if ((int)u < 0) len = 7;
      else if (u  < 256) len = 2;
      else len = 5;
      break;

    case ERL_LONGLONG:
      l = ep->uval.llval.i;
      if ((l > ((long long) INT_MAX)) || 
         (l < ((long long) INT_MIN))) len = 11;
      else if ((l < 256) && (l >= 0)) len = 2; 
      else len = 5;
      break;

    case ERL_U_LONGLONG:
      ul = ep->uval.ullval.u;
      if (ul > ((unsigned long long) INT_MAX)) len = 11;
      else if (ul  < 256) len = 2;
      else len = 5;
      break;

    case ERL_PID:
      len = 1 + atom_len_helper(&ep->uval.pidval.node) + 4 + 4 + 1;
      break;

    case ERL_REF:
      len = 1 + 2 + atom_len_helper(&ep->uval.refval.node) + 1 + ERL_REF_LEN(ep) * 4;
      break;

    case ERL_PORT:
      len = 1 + atom_len_helper(&ep->uval.portval.node) + 4 + 1;
      break;

    case ERL_EMPTY_LIST:
      len = 1;
      break;

    case ERL_LIST:
      i = is_string(ep);
      if ((i > 0) && (i < 0x10000)) { /* string: 3 + strlen */
	for (len = 3; ERL_TYPE(ep) == ERL_LIST; ep =  TAIL(ep)) {
	  len++;
	}
      }
      else { /* list: 5 + len(elem1) + len(elem2) ... */
	for (len = 5; ERL_TYPE(ep) == ERL_LIST; ep =  TAIL(ep)) {
	  len += erl_term_len_helper(HEAD(ep), dist);
	}
	len += erl_term_len_helper(ep, dist); /* last element */
      }
      break;

    case ERL_TUPLE:
      /* (2 or 5) + len(elem1) + len(elem2) ... */
      i = ep->uval.tval.size;
      if (i <= 0xff) len = 2;
      else len = 5;
      
      for (i=0; i<ep->uval.tval.size; i++) {
	len += erl_term_len_helper(ep->uval.tval.elems[i], dist);
      }
      break;

    case ERL_FLOAT:
      len = 32;
      break;

    case ERL_BINARY:
      i = ep->uval.bval.size;
      len = 5 + i;
      break;

    case ERL_FUNCTION:
      if (ERL_FUN_ARITY(ep) == -1) {
	  len = 1 + 4;
	  len += erl_term_len_helper(ERL_FUN_CREATOR(ep),dist);
	  len += erl_term_len_helper(ERL_FUN_MODULE(ep),dist);
	  len += erl_term_len_helper(ERL_FUN_INDEX(ep),dist);
	  len += erl_term_len_helper(ERL_FUN_UNIQ(ep),dist);
	  for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
	      len += erl_term_len_helper(ERL_CLOSURE_ELEMENT(ep,i), dist);
      } else {
	  len = 1 + 4 + 16 + 4 + 4;
	  len += erl_term_len_helper(ERL_FUN_MODULE(ep),dist);
	  len += erl_term_len_helper(ERL_FUN_INDEX(ep),dist);
	  len += erl_term_len_helper(ERL_FUN_UNIQ(ep),dist);
	  len += erl_term_len_helper(ERL_FUN_CREATOR(ep),dist);
	  for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
	      len += erl_term_len_helper(ERL_CLOSURE_ELEMENT(ep,i), dist);
      }
      break;

    default:
#ifdef DEBUG
	fprintf(stderr, "Shouldn't happen: erl_term_len, unknown term type: '%c'\n",ERL_TYPE(ep));
#endif
      erl_errno = EINVAL;
      exit(1);
    }
  }

  return len;
}

/* 
 * This one makes it easy to ENCODE several CONSECUTIVE
 * ETERM's into the same buffer. 
 */
int erl_encode_buf(ETERM *ep, unsigned char **ext)
{
  unsigned char *start=*ext;
  
  *(*ext)++ = ERL_VERSION_MAGIC;
  if (erl_encode_it(ep, ext, 0)) {
#ifdef DEBUG
    erl_err_msg("<ERROR> erl_encode_buf: Error while encoding\n");
#endif
    return 0;
  }
  return (*ext - start);

} /* erl_encode_buf */


static int read_atom(unsigned char** ext, Erl_Atom_data* a)
{
    char buf[MAXATOMLEN_UTF8];
    int offs = 0;
    erlang_char_encoding enc;
    int ret = ei_decode_atom_as((char*)*ext, &offs, buf, MAXATOMLEN_UTF8,
				ERLANG_LATIN1|ERLANG_UTF8, NULL, &enc);
    *ext += offs;

    if (ret == 0) {
	int i = strlen(buf);
	char* clone = erl_malloc(i+1);
	memcpy(clone, buf, i+1);

	a->latin1 = NULL; 	    
	a->lenL = 0;
	a->utf8 = NULL;
	a->lenU = 0;
	if (enc & (ERLANG_LATIN1 | ERLANG_ASCII)) {
	    a->latin1 = clone; 	    
	    a->lenL = i;
	}
	if (enc & (ERLANG_UTF8 | ERLANG_ASCII)) {
	    a->utf8 = clone;
	    a->lenU = i;
	}
    }
    return ret;
}

/*
 * The actual DECODE engine.
 * Returns NULL in case of failure.
 */
static ETERM *erl_decode_it(unsigned char **ext)
{
    char *cp;
    ETERM *ep,*tp,*np;
    unsigned int u,sign;
    int i,j,arity;
    double ff;
    unsigned char tag;
    
    /* Assume we are going to decode an integer */
    ep = erl_alloc_eterm(ERL_INTEGER);
    ERL_COUNT(ep) = 1;

    tag = *(*ext)++;
    switch (tag)
    {
    case ERL_INTEGER_EXT:
	i = (int) (**ext << 24) | ((*ext)[1] << 16) |
	    ((*ext)[2] << 8) | (*ext)[3];
	*ext += 4;
	ep->uval.ival.i = i;
	return ep;

    case ERL_SMALL_INTEGER_EXT:
	i = *(*ext)++;
	ep->uval.ival.i = i;
	return ep;

        /* NOTE: The arity below for bigs is not really the arity (= number of digits) */
        /*       It is the byte count and this might cause problems in other parts...  */
    case ERL_SMALL_BIG_EXT:
        arity = *(*ext)++; 
	goto big_cont;
    case ERL_LARGE_BIG_EXT:
	arity = (**ext << 24) | ((*ext)[1])<< 16 | 
	    ((*ext)[2]) << 8 |((*ext)[3]); 
	*ext += 4;
    big_cont:

#ifdef _MSC_VER
#define MAX_TO_NEGATE 0x8000000000000000Ui64
#else
#define MAX_TO_NEGATE 0x8000000000000000ULL
#endif

	sign = *(*ext)++; 
	if (arity > 8)             
	    goto big_truncate;

	if (arity == 4 && ((*ext)[3] & 0x80) && !sign) {
	    /* It will fit into an unsigned int !! */
	    u = (((*ext)[3] << 24)|((*ext)[2])<< 16|((*ext)[1]) << 8 |(**ext));
	    ERL_TYPE(ep) = ERL_U_INTEGER;
	    ep->uval.uival.u = u;
	    /* *ext += i; */
	    *ext += arity;
	    return ep;
	} else if (arity == 4 && !((*ext)[3] & 0x80)) {
	    /* It will fit into an int !! 
	     */
	    i = (int) (((*ext)[3] << 24) | ((*ext)[2])<< 16 |
		       ((*ext)[1]) << 8 | (**ext));
	    if (sign) i = -i;
	    ERL_TYPE(ep) = ERL_INTEGER;
	    ep->uval.ival.i = i;
	    *ext += arity;
	    return ep;
	} else if (arity == 8 && ((*ext)[7] & 0x80) && !sign) {
	    /* Fits in an unsigned long long */
	    int x;
	    unsigned long long ul = 0LL;

	    for(x = 0 ; x < arity ; x++) {
		ul |= ((unsigned long long)(*ext)[x]) << ((unsigned long long)(8*x));
	    }
	   
	    ERL_TYPE(ep) = ERL_U_LONGLONG;
	    ep->uval.ullval.u = ul;
	    *ext += arity;
	    return ep;
	} else {
            /* Fits in a signed long long */
            int x;
            unsigned long long l = 0LL;
            long long sl;

            for(x = 0 ; x < arity ; x++) {
                l |= ((unsigned long long)(*ext)[x]) << ((unsigned long long)(8*x));
            }

            sl = (long long)l;

            if (sign && l != MAX_TO_NEGATE) {
                sl = -sl;
                if (sl > 0) goto big_truncate;
            }

            ERL_TYPE(ep) = ERL_LONGLONG;
            ep->uval.llval.i = sl;
            *ext += arity;
            return ep;
	}
#undef MAX_TO_NEGATE
    big_truncate: 
	/* truncate to: (+/-) 1 */
#ifdef DEBUG
	erl_err_msg("<WARNING> erl_decode_it: Integer truncated...");
#endif
	ERL_TYPE(ep) = ERL_INTEGER;
	ep->uval.ival.i = sign?-1:1;
	*ext += arity;
	return ep;
      
    case ERL_ATOM_EXT:
    case ERL_SMALL_ATOM_EXT:
    case ERL_ATOM_UTF8_EXT:
    case ERL_SMALL_ATOM_UTF8_EXT:
	
	ERL_TYPE(ep) = ERL_ATOM;
	--(*ext);
	if (read_atom(ext, &ep->uval.aval.d) < 0) return NULL;
	return ep;
      
    case ERL_PID_EXT:
    case ERL_NEW_PID_EXT:
	{
	    unsigned int number, serial;
	    unsigned int creation;

	    ERL_TYPE(ep) = ERL_PID;
	    if (read_atom(ext, &ep->uval.pidval.node) < 0) return NULL;

	    /* get the integers */
	    number = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | 
		((*ext)[2]) << 8 | ((*ext)[3]);	
	    *ext += 4;
	    serial = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | 
		((*ext)[2]) << 8 | ((*ext)[3]);	
	    *ext += 4;
            if (tag == ERL_PID_EXT)
                creation =  *(*ext)++;
            else {
                creation = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
                    ((*ext)[2]) << 8 | ((*ext)[3]);
                *ext += 4;
            }
	    erl_mk_pid_helper(ep, number, serial, creation);
	    return ep;
	}
    case ERL_REFERENCE_EXT:
	{
	    unsigned int n[3] = {0, 0, 0};
	    unsigned char creation;

	    ERL_TYPE(ep) = ERL_REF;
	    if (read_atom(ext, &ep->uval.refval.node) < 0) return NULL;

	    /* get the integers */
	    n[0] = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | 
		((*ext)[2]) << 8 | ((*ext)[3]);	
	    *ext += 4;
	    creation =  *(*ext)++; 
	    __erl_mk_reference(ep, NULL, 1, n, creation);
	    return ep;
	}

    case ERL_NEW_REFERENCE_EXT:
    case ERL_NEWER_REFERENCE_EXT:
	{
	    size_t cnt, i;
	    unsigned int n[3];
	    unsigned int creation;

	    ERL_TYPE(ep) = ERL_REF;
	    cnt = ((*ext)[0] << 8) | (*ext)[1];
	    *ext += 2;

	    if (read_atom(ext, &ep->uval.refval.node) < 0) return NULL;

	    /* get the integers */
            if (tag == ERL_NEW_REFERENCE_EXT)
                creation =  *(*ext)++;
            else {
                creation = ((*ext)[0] << 24) | ((*ext)[1]) << 16 |
                    ((*ext)[2]) << 8 | ((*ext)[3]);
                *ext += 4;
            }
	    for(i = 0; i < cnt; i++)
	    {
		n[i] = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | 
		    ((*ext)[2]) << 8 | ((*ext)[3]);	
		*ext += 4;
	    }
	    __erl_mk_reference(ep, NULL, cnt, n, creation);
	    return ep;
	}

    case ERL_PORT_EXT:
    case ERL_NEW_PORT_EXT:
	{
	    unsigned int number;
	    unsigned int creation;

	    ERL_TYPE(ep) = ERL_PORT;
	    if (read_atom(ext, &ep->uval.portval.node) < 0) return NULL;

	    /* get the integers */
	    number = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | 
		((*ext)[2]) << 8 | ((*ext)[3]);	
	    *ext += 4;
            if (tag == ERL_PORT_EXT)
                creation =  *(*ext)++;
            else {
                creation = (((*ext)[0] << 24) | ((*ext)[1]) << 16 |
                            ((*ext)[2]) << 8  | ((*ext)[3]));
                *ext += 4;
            }
	    erl_mk_port_helper(ep, number, creation);
	    return ep;
	}

    case ERL_NIL_EXT:
	ERL_TYPE(ep) = ERL_EMPTY_LIST;
	return ep;

    case ERL_LIST_EXT:
	ERL_TYPE(ep) = ERL_LIST;
	i = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
	*ext += 4;	
	/* ASSERT(i != 0);	*/	/* Should be represented by ERL_NIL_EXT. */
	tp = ep;
	for (j = 0; j < i; j++) 
	    if ((HEAD(tp) = erl_decode_it(ext)) == NULL) 
		goto failure;
	    else if (j + 1 < i) {
		/* We have to watch out for how we allocates the
		 * last tail element since we may encounter non-
		 * well formed lists.
		 */
		np = erl_alloc_eterm(ERL_LIST);
		ERL_COUNT(np) = 1;
                TAIL(np) = NULL; /* in case of failure */
		TAIL(tp) = np;
		tp = np;
	    }
	if ((TAIL(tp) = erl_decode_it(ext)) == NULL) 
	    goto failure;
	return ep;

    case ERL_STRING_EXT:
	{
	    unsigned char* s;
	  
	    ERL_TYPE(ep) = ERL_EMPTY_LIST;
	    i = (**ext << 8) | ((*ext)[1]);
	    *ext += 2;
	    s = *ext+i;

	    while (*ext < s) {
		ETERM* integer;
		ETERM* cons;

		integer = erl_alloc_eterm(ERL_INTEGER);
		ERL_COUNT(integer) = 1;
		integer->uval.ival.i = *--s;

		cons = erl_alloc_eterm(ERL_LIST);
		ERL_COUNT(cons) = 1;
		HEAD(cons) = integer;
		TAIL(cons) = ep;
		ep = cons;
	    }
	    *ext += i;
	    return ep;
	}

    case ERL_SMALL_TUPLE_EXT:
	ERL_TYPE(ep) = ERL_TUPLE;
	i = *(*ext)++;
	goto decode_tuple;

    case ERL_LARGE_TUPLE_EXT:
	i = (**ext << 24) | ((*ext)[1]) << 16 | 
	    ((*ext)[2]) << 8 | ((*ext)[3]) ;	
	*ext += 4;
    decode_tuple:
	ep->uval.tval.size = i;
	j = (i + 1) * sizeof(ETERM*);
	ep->uval.tval.elems = (ETERM**) erl_malloc(j);
	memset(ep->uval.tval.elems, 0, j); /* in case of failure below... */
	for (i=0; i<ep->uval.tval.size; i++)
	    if ((tp = erl_decode_it(ext)) == NULL)
		goto failure;
	    else
		ep->uval.tval.elems[i] = tp;
	return ep;

    case ERL_FLOAT_EXT:
    case NEW_FLOAT_EXT:
	ERL_TYPE(ep) = ERL_FLOAT;
	cp = (char *) *ext;
	i = -1;
	if (ei_decode_double(cp, &i, &ff) == -1)
	    goto failure;
	*ext += i;
	ep->uval.fval.f = ff;
	return ep;

    case ERL_BINARY_EXT:
	ERL_TYPE(ep) = ERL_BINARY;
	i = (**ext << 24) | ((*ext)[1] << 16) |
	    ((*ext)[2] << 8) | (*ext)[3];
	*ext += 4;
	ep->uval.bval.size = i;
	ep->uval.bval.b = (unsigned char *) erl_malloc(i);
	memcpy(ep->uval.bval.b, *ext, i);
	*ext += i;
	return ep;

    case ERL_FUN_EXT:		/* FIXME: error checking */
	ERL_TYPE(ep) = ERL_FUNCTION;
	i = get32be(*ext);
	/*i = *(**ext << 24) | ((*ext)[1] << 16) | ((*ext)[2] << 8) | (*ext)[3];
	 *ext += 4; */
	ERL_FUN_ARITY(ep) = -1;
	ERL_CLOSURE_SIZE(ep) = i;
	ERL_FUN_CREATOR(ep) = erl_decode_it(ext);
	ERL_FUN_MODULE(ep) = erl_decode_it(ext);
	ERL_FUN_INDEX(ep) = erl_decode_it(ext);
	ERL_FUN_UNIQ(ep) = erl_decode_it(ext);
	j = i * sizeof(ETERM*);
	ERL_CLOSURE(ep) = (ETERM**) erl_malloc(j);
	memset(ERL_CLOSURE(ep), 0, j);
	for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
	    ERL_CLOSURE_ELEMENT(ep,i) = erl_decode_it(ext);
	return ep;

    case ERL_NEW_FUN_EXT:	/* FIXME: error checking */
	ERL_TYPE(ep) = ERL_FUNCTION;
	i = get32be(*ext);	/* size, we don't use it here */
	ERL_FUN_ARITY(ep) = get8(*ext);
	memcpy(ERL_FUN_MD5(ep), *ext, 16);
	*ext += 16;
	ERL_FUN_NEW_INDEX(ep) = get32be(*ext);
	i = get32be(*ext);
	ERL_CLOSURE_SIZE(ep) = i;
	ERL_FUN_MODULE(ep) = erl_decode_it(ext);
	ERL_FUN_INDEX(ep) = erl_decode_it(ext);
	ERL_FUN_UNIQ(ep) = erl_decode_it(ext);
	ERL_FUN_CREATOR(ep) = erl_decode_it(ext);
	j = i * sizeof(ETERM*);
	ERL_CLOSURE(ep) = (ETERM**) erl_malloc(j);
	memset(ERL_CLOSURE(ep), 0, j);
	for (i = 0; i < ERL_CLOSURE_SIZE(ep); i++)
	    ERL_CLOSURE_ELEMENT(ep,i) = erl_decode_it(ext);
	return ep;

    } /* switch */
    
 failure:
    erl_free_term(ep);
    return (ETERM *) NULL;
    
} /* erl_decode_it */

/*
 * DECODE a buffer of BYTES into an ETERM.
 * Returns NULL in case of failure.
 */
ETERM *erl_decode(unsigned char *t) 
{
  ETERM *ep;
  unsigned char *ext;

  ext = t;

  /* We ignore the version magic since it might be
   * possible that the buffer has been manipulated
   * with erl_peek_ext.
   */
  if (*ext == ERL_VERSION_MAGIC) 
    ext++;  

  ep = NULL;
  ep = erl_decode_it(&ext);
#ifdef DEBUG
  if (!ep) erl_err_msg("<ERROR> erl_decode: Error while decoding");
#endif
  return ep;

} /* erl_decode */

/* 
 * This one makes it possible to DECODE two CONSECUTIVE 
 * ETERM's in the same buffer. 
 */
ETERM *erl_decode_buf(unsigned char **ext) 
{
  ETERM *ep;
  
  /* We ignore the version magic since it might be
   * possible that the buffer has been manipulated
   * with erl_peek_ext.
   */
  if (**ext == ERL_VERSION_MAGIC) 
    (*ext)++;

  ep = NULL;
  ep = erl_decode_it(ext);
#ifdef DEBUG
    if (!ep) erl_err_msg("<ERROR> erl_decode_buf: Error while decoding");
#endif
  return ep;

} /* erl_decode_buf */


/*==============================================================
 * Ok, here comes routines for inspecting/manipulating 
 * an encoded buffer of bytes.
 *==============================================================
 */

/*
 * Return 1 if the VERSION MAGIC in the BUFFER is the
 * same as the this library version.
 */
int erl_verify_magic(unsigned char *ext)
{

  if (*ext == ERL_VERSION_MAGIC) 
    return 1;
  else
    return 0;

} /* erl_verify_magic */

/*
 * Return the TYPE of an ENCODED ETERM.
 * At failure, return 0.
 */ 
unsigned char erl_ext_type(unsigned char *ext)
{
    /* FIXME old code could skip multiple magic */

    /* Move over magic number if any */
    if (*ext == ERL_VERSION_MAGIC) ext++;
  
    switch (*ext) {
    case ERL_SMALL_INTEGER_EXT:
    case ERL_INTEGER_EXT:
	return ERL_INTEGER;
    case ERL_ATOM_EXT:
    case ERL_ATOM_UTF8_EXT:
    case ERL_SMALL_ATOM_EXT:
    case ERL_SMALL_ATOM_UTF8_EXT:
	return ERL_ATOM;
    case ERL_PID_EXT:
    case ERL_NEW_PID_EXT:
	return ERL_PID;
    case ERL_PORT_EXT:
    case ERL_NEW_PORT_EXT:
	return ERL_PORT;
    case ERL_REFERENCE_EXT:
    case ERL_NEW_REFERENCE_EXT:
    case ERL_NEWER_REFERENCE_EXT:
	return ERL_REF;
    case ERL_NIL_EXT: 
	return ERL_EMPTY_LIST;
    case ERL_LIST_EXT:
	return ERL_LIST;
    case ERL_SMALL_TUPLE_EXT:
    case ERL_LARGE_TUPLE_EXT:
	return ERL_TUPLE;
    case ERL_FLOAT_EXT:
    case NEW_FLOAT_EXT:
	return ERL_FLOAT;
    case ERL_BINARY_EXT:
	return ERL_BINARY;
    case ERL_FUN_EXT:
    case ERL_NEW_FUN_EXT:
	return ERL_FUNCTION;
    case ERL_SMALL_BIG_EXT:
    case ERL_LARGE_BIG_EXT:
        return ERL_BIG;
    default:
	return 0;

    } /* switch */

} /* erl_ext_type */

/* 
 * Returns the number of elements in compund
 * terms. For other kind of terms zero is returned.
 * At failure -1 is returned.
 */
int erl_ext_size(unsigned char *t)
{
    int i;
    unsigned char *v;

    if (*t == ERL_VERSION_MAGIC) 
	return erl_ext_size(t+1);
 
    v = t+1;
    switch(*t) {
    case ERL_SMALL_INTEGER_EXT:
    case ERL_INTEGER_EXT:
    case ERL_ATOM_EXT:
    case ERL_ATOM_UTF8_EXT:
    case ERL_SMALL_ATOM_EXT:
    case ERL_SMALL_ATOM_UTF8_EXT:
    case ERL_PID_EXT:
    case ERL_NEW_PID_EXT:
    case ERL_PORT_EXT:
    case ERL_NEW_PORT_EXT:
    case ERL_REFERENCE_EXT:
    case ERL_NEW_REFERENCE_EXT:
    case ERL_NEWER_REFERENCE_EXT:
    case ERL_NIL_EXT: 
    case ERL_BINARY_EXT:
    case ERL_STRING_EXT:
    case ERL_FLOAT_EXT:
    case NEW_FLOAT_EXT:
    case ERL_SMALL_BIG_EXT:
    case ERL_LARGE_BIG_EXT:
	return 0;
	break;
    case ERL_SMALL_TUPLE_EXT:
	i = v[0];
	return i;
	break;
    case ERL_LIST_EXT:
    case ERL_LARGE_TUPLE_EXT:
	i = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3];
	return i;
	break;
    case ERL_FUN_EXT:
	i = (v[0] << 24) | (v[1] << 16) | (v[2] << 8) | v[3];
	return i+4;
	break;
    case ERL_NEW_FUN_EXT:
        v += 4 + 1 + 16 + 4;
	i = get32be(v);
	return i + 4;
	break;
    default:
	return -1;
	break;
    } /* switch */

} /* ext_size */


static int jump_atom(unsigned char** ext)
{
    unsigned char* e = *ext;
    int len;

    switch (*e++) {
    case ERL_ATOM_EXT:
    case ERL_ATOM_UTF8_EXT:
	len = (e[0] << 8) | e[1];
	e += (len + 2);
	break;

    case ERL_SMALL_ATOM_EXT:
    case ERL_SMALL_ATOM_UTF8_EXT:
	len = e[0];
	e += (len + 1);
	break;

    default:
	return 0;
    }
    *ext = e;
    return 1;
}


/*
 * MOVE the POINTER PAST the ENCODED ETERM we
 * are currently pointing at. Returns 1 at
 * success, otherwise 0.
 */
static int jump(unsigned char **ext) 
{
    int j,k,i=0;
    int n;
    const int tag = *(*ext)++;
    
    switch (tag) {
    case ERL_VERSION_MAGIC:
	return jump(ext);
    case ERL_INTEGER_EXT:
	*ext += 4;
	break;
    case ERL_SMALL_INTEGER_EXT:
	*ext += 1;
	break;
    case ERL_ATOM_EXT:
    case ERL_ATOM_UTF8_EXT:
    case ERL_SMALL_ATOM_EXT:
    case ERL_SMALL_ATOM_UTF8_EXT:
	jump_atom(ext);
	break;
    case ERL_PID_EXT:
	if (!jump_atom(ext)) return 0;
	*ext += 4 + 4 + 1;
	break;
    case ERL_NEW_PID_EXT:
        if (!jump_atom(ext)) return 0;
        *ext += 4 + 4 + 4;
        break;
    case ERL_REFERENCE_EXT:
    case ERL_PORT_EXT:
	if (!jump_atom(ext)) return 0;
	*ext += 4 + 1;
	break;
    case ERL_NEW_PORT_EXT:
        if (!jump_atom(ext)) return 0;
        *ext += 4 + 4;
        break;
    case ERL_NEW_REFERENCE_EXT:
    case ERL_NEWER_REFERENCE_EXT:
	n = (**ext << 8) | (*ext)[1];
	*ext += 2;
	/* first field is an atom */
	if (!jump_atom(ext)) return 0;
	*ext += 4*n + (tag == ERL_NEW_REFERENCE_EXT ? 1 : 4);
	break;
    case ERL_NIL_EXT:
	/* We just passed it... */
	break;
    case ERL_LIST_EXT:
	i = j = 0;
	j = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
	*ext += 4;	
	for(k=0; k<j; k++) 
	    if ((i = jump(ext)) == 0)
		return(0);
	if (**ext == ERL_NIL_EXT) {
	    *ext += 1;
	    break;
	}
	if (jump(ext) == 0) return 0;
	break;
    case ERL_STRING_EXT:
	i = **ext << 8 | (*ext)[1];
	*ext += 2 + i;
	break;
    case ERL_SMALL_TUPLE_EXT:
	i = *(*ext)++;
	goto jump_tuple;
    case ERL_LARGE_TUPLE_EXT:
	i = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
	*ext += 4;
    jump_tuple:
	for (j = 0; j < i; j++) 
	    if ((k = jump(ext)) == 0)
		return(0);
	break;
    case ERL_FLOAT_EXT:
	*ext += 31;
	break;
    case NEW_FLOAT_EXT:
	*ext += 8;
	break;
    case ERL_BINARY_EXT:
	i = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
	*ext += 4+i;
	break;
    case ERL_FUN_EXT:
	i = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
	*ext += 4;
	i += 4;
	for (j = 0; j < i; j++)
	    if ((k = jump(ext)) == 0)
		return(0);
	break;
    case ERL_NEW_FUN_EXT:
	i = get32be(*ext);
	*ext += i + 4;
	break;
    case ERL_SMALL_BIG_EXT:
        i = *(*ext);
        *ext += i + 1;
        break;
    case ERL_LARGE_BIG_EXT:
	i = get32be(*ext);
        *ext += i + 4;
        break;
    default:
	return 0;
    } /* switch */

    return 1;

} /* jump */

/* 
 * The actual PEEK engine.
 */
static unsigned char *peek_ext(unsigned char **ext, int jumps)
{
  int i;

  switch (*(*ext)++) 
    {
    case ERL_VERSION_MAGIC:
      return peek_ext(ext, jumps);
    case ERL_SMALL_TUPLE_EXT:
      i = *(*ext)++;
      goto do_the_peek_stuff;
    case ERL_LARGE_TUPLE_EXT:
    case ERL_LIST_EXT:
      i = (**ext << 24) | ((*ext)[1]) << 16| ((*ext)[2]) << 8| ((*ext)[3]) ;  
      *ext += 4;
    do_the_peek_stuff:
      if (i <= jumps)   {
#ifdef DEBUG
	erl_err_msg("<ERROR> peek_ext: Out of range"); 
#endif
	return NULL;
      }
      for(i=0; i<jumps; i++)
	if (!jump(ext)) {
#ifdef DEBUG
	  erl_err_msg("<ERROR> peek_ext: Bad data"); 
#endif
	  return NULL;
	}
      return *ext;
    default:
#ifdef DEBUG
      erl_err_msg("<ERROR> peek_ext: Can't peek in non list/tuple type");
#endif
      return NULL;
    } /* switch */

} /* peek_ext */
	
/*
 * Return a POINTER TO the N:TH ELEMENT in a
 * COMPUND ENCODED ETERM.
 */
unsigned char *erl_peek_ext(unsigned char *ext, int jumps)
{
  unsigned char *x=ext;

  return peek_ext(&x, jumps);  

} /* erl_peek_ext */

/* 
 * Lexically compare two strings of bytes,
 * (string s1 length l1 and s2 l2).
 * Return: -1 if s1 < s2
 *	    0 if s1 = s2
 *	    1 if s1 > s2 
 */
static int cmpbytes(unsigned char* s1,int l1,unsigned char* s2,int l2)
{
  int i;
  i = 0;
  while((i < l1) && (i < l2)) {
    if (s1[i] < s2[i]) return(-1);
    if (s1[i] > s2[i]) return(1);
    i++;
  }
  if (l1 < l2) return(-1);
  if (l1 > l2) return(1);
  return(0);

} /* cmpbytes */

#define tag2enc(T) ((T)==ERL_ATOM_EXT || (T)==ERL_SMALL_ATOM_EXT ? ERLANG_LATIN1 : ERLANG_UTF8)

static int cmpatoms(unsigned char* s1, int l1, unsigned char tag1,	 
                    unsigned char* s2, int l2, unsigned char tag2)
{
    erlang_char_encoding enc1 = tag2enc(tag1);
    erlang_char_encoding enc2 = tag2enc(tag2);

    if (enc1 == enc2) {
	return cmpbytes(s1, l1,s2,l2);
    }
    
    if (enc1 == ERLANG_LATIN1) {
	return cmp_latin1_vs_utf8((char*)s1, l1, (char*)s2, l2);
    }
    else {
	return -cmp_latin1_vs_utf8((char*)s2, l2, (char*)s1, l1);
    }
}

int cmp_latin1_vs_utf8(const char* strL, int lenL, const char* strU, int lenU)
{
    unsigned char* sL = (unsigned char*)strL;
    unsigned char* sU = (unsigned char*)strU;
    unsigned char* sL_end = sL + lenL;
    unsigned char* sU_end = sU + lenU;

    while(sL < sL_end && sU < sU_end) {
	unsigned char UasL;
	if (*sL >= 0x80) {
	    if (*sU < 0xC4 && (sU+1) < sU_end) {
		UasL = ((sU[0] & 0x3) << 6) | (sU[1] & 0x3F);
	    }
	    else return -1;
	}
	else {
	    UasL = *sU;
	}
	if (*sL < UasL) return -1;
	if (*sL > UasL) return 1;

	sL++;
	if (*sU < 0x80) sU++;
	else if (*sU < 0xE0) sU += 2;
	else if (*sU < 0xF0) sU += 3;
	else /*if (*sU < 0xF8)*/ sU += 4;
    }

    return (sU >= sU_end) - (sL >= sL_end); /* -1, 0 or 1 */
}


#define CMP_EXT_ERROR_CODE 4711

#define CMP_EXT_INT32_BE(AP, BP)				\
do {								\
    if ((AP)[0] != (BP)[0]) return (AP)[0] < (BP)[0] ? -1 : 1;	\
    if ((AP)[1] != (BP)[1]) return (AP)[1] < (BP)[1] ? -1 : 1;	\
    if ((AP)[2] != (BP)[2]) return (AP)[2] < (BP)[2] ? -1 : 1;	\
    if ((AP)[3] != (BP)[3]) return (AP)[3] < (BP)[3] ? -1 : 1;	\
} while (0)

#define CMP_EXT_SKIP_ATOM(EP)					\
do {								\
    if (!jump_atom(&(EP)))					\
	return CMP_EXT_ERROR_CODE;				\
} while (0)

/* 
 * We now know that both byte arrays are of the same type.
 */
static int compare_top_ext(unsigned char**, unsigned char **); /* forward */
static int cmp_exe2(unsigned char **e1, unsigned char **e2);

static int cmp_refs(unsigned char **e1, unsigned char **e2)
{
    int tmp, n1, n2;
    unsigned char *node1, *node2, *id1, *id2, cre1, cre2;

    if (*((*e1)++) == ERL_REFERENCE_EXT) {
	node1 = *e1;
	CMP_EXT_SKIP_ATOM(*e1);
	n1 = 1;
	id1 = *e1;
	cre1 = (*e1)[4];
	*e1 += 5;
    } else {
	n1 = get16be(*e1);
	node1 = *e1;
	CMP_EXT_SKIP_ATOM(*e1);
	cre1 = **e1;
	id1 = (*e1) + 1 + (n1 - 1)*4;
	*e1 = id1 + 4;
    }

    if (*((*e2)++) == ERL_REFERENCE_EXT) {
	node2 = *e2;
	CMP_EXT_SKIP_ATOM(*e2);
	n2 = 1;
	id2 = *e2;
	cre2 = (*e2)[4];
	*e2 += 5;
    } else {
	n2 = get16be(*e2);
	node2 = *e2;
	CMP_EXT_SKIP_ATOM(*e2);
	cre2 = **e2;
	id2 = (*e2) + 1 + (n2 - 1)*4;
	*e2 = id2 + 4;
    }

    /* First compare node names... */
    tmp = cmp_exe2(&node1, &node2);
    if (tmp != 0)
	return tmp;

    /* ... then creations ... */
    if (cre1 != cre2)
	return cre1 < cre2 ? -1 : 1;

    /* ... and then finally ids. */
    if (n1 != n2) {
	unsigned char zero[] = {0, 0, 0, 0};
	if (n1 > n2)
	    do {
		CMP_EXT_INT32_BE(id1, zero);
		id1 -= 4;
		n1--;
	    } while (n1 > n2);
	else
	    do {
		CMP_EXT_INT32_BE(zero, id2);
		id2 -= 4;
		n2--;
	    } while (n2 > n1);
    }
    
    for (; n1 > 0; n1--, id1 -= 4, id2 -= 4)
	CMP_EXT_INT32_BE(id1, id2);

    return 0;
}

static int cmp_string_list(unsigned char **e1, unsigned char **e2) {
  
  /* we need to compare a string in **e1 and a list in **e2               */
  /* convert the string to list representation and convert that with e2   */
  /* we need a temporary buffer of:                                       */
  /* 5 (list tag + length) + 2*string length + 1 (end of list tag)        */
  /* for short lists we use a stack allocated buffer, otherwise we malloc */

  unsigned char *bp;
  unsigned char buf[5+2*255+1]; /* used for short lists */
  int i,e1_len;
  int res;
  
  e1_len = ((*e1)[1] << 8) | ((*e1)[2]);
  if ( e1_len < 256 ) {
    bp = buf;
  } else {
    bp = erl_malloc(5+(2*e1_len)+1);
  }

  bp[0] = ERL_LIST_EXT;
  bp[1] = bp[2] = 0;
  bp[3] = (*e1)[1];
  bp[4] = (*e1)[2];

  for(i=0;i<e1_len;i++) {
    bp[5+2*i] = ERL_SMALL_INTEGER_EXT;
    bp[5+2*i+1] = (*e1)[3+i];
  }

  bp[5+2*e1_len] = ERL_NIL_EXT;

  res = cmp_exe2(&bp, e2);

  if ( e1_len >= 256 ) free(bp);

  return res;
}

static int cmp_exe2(unsigned char **e1, unsigned char **e2)
{
  int min,  ret,i,j,k;
  double ff1, ff2;
  unsigned char tag1, tag2;

  if ( ((*e1)[0] == ERL_STRING_EXT) && ((*e2)[0] == ERL_LIST_EXT) ) {
    return cmp_string_list(e1, e2);
  } else if ( ((*e1)[0] == ERL_LIST_EXT) && ((*e2)[0] == ERL_STRING_EXT) ) {
    return -cmp_string_list(e2, e1);
  }

  tag1 = *(*e1)++;
  tag2 = *(*e2)++;
  i = j = 0;
  switch (tag1) 
    {
    case ERL_SMALL_INTEGER_EXT:
      if (**e1 < **e2) ret = -1;
      else if (**e1 > **e2) ret = 1;
      else ret = 0;
      *e1 += 1; *e2 += 1;
      return ret;
    case ERL_INTEGER_EXT:
      i = (int) (**e1 << 24) | ((*e1)[1] << 16) |((*e1)[2] << 8) | (*e1)[3];
      j = (int) (**e2 << 24) | ((*e2)[1] << 16) |((*e2)[2] << 8) | (*e2)[3];
      if ( i < j) 
	ret = -1;
      else if ( i > j) 
	ret = 1;
      else 
	ret = 0;
      *e1 += 4; *e2 += 4;
      return ret;
    case ERL_ATOM_EXT:
    case ERL_ATOM_UTF8_EXT:
      i = (**e1) << 8; (*e1)++;
      j = (**e2) << 8; (*e2)++;
      /*fall through*/
    case ERL_SMALL_ATOM_EXT:
    case ERL_SMALL_ATOM_UTF8_EXT:
      i |= (**e1); (*e1)++;
      j |= (**e2); (*e2)++;
      ret = cmpatoms(*e1, i, tag1, *e2, j, tag2);
      *e1 += i;
      *e2 += j;
      return ret;
    case ERL_PID_EXT:
    case ERL_NEW_PID_EXT: {
      erlang_pid pid1, pid2;
      unsigned char* buf1 = *e1 - 1;
      unsigned char* buf2 = *e2 - 1;
      int ix1 = 0, ix2 = 0;

      if (ei_decode_pid((char*)buf1, &ix1, &pid1) ||
          ei_decode_pid((char*)buf2, &ix2, &pid2))
          return CMP_EXT_ERROR_CODE;

      *e1 = buf1 + ix1;
      *e2 = buf2 + ix2;

      /* First compare serials ... */
      if      (pid1.serial < pid2.serial) return -1;
      else if (pid1.serial > pid2.serial) return 1;

      /* ... then ids ... */
      if      (pid1.num < pid2.num) return -1;
      else if (pid1.num > pid2.num) return 1;

      /* ... then node names ... */
      j = strcmp(pid1.node, pid2.node);
      if      (j < 0) return -1;
      else if (j > 0) return 1;

      /* ... and then finaly creations. */
      if      (pid1.creation < pid2.creation) return -1;
      else if (pid1.creation > pid2.creation) return 1;

      return 0;
    }
    case ERL_PORT_EXT:
    case ERL_NEW_PORT_EXT: {
        erlang_port port1, port2;
        unsigned char* buf1 = *e1 - 1;
        unsigned char* buf2 = *e2 - 1;
        int ix1 = 0, ix2 = 0;

        if (ei_decode_port((char*)buf1, &ix1, &port1) ||
            ei_decode_port((char*)buf2, &ix2, &port2))
            return CMP_EXT_ERROR_CODE;

        *e1 = buf1 + ix1;
        *e2 = buf2 + ix2;

        /* First compare node names ... */
        j = strcmp(port1.node, port2.node);
        if      (j < 0) return -1;
        else if (j > 0) return 1;

      /* ... then creations ... */
        if      (port1.creation < port2.creation) return -1;
        else if (port1.creation > port2.creation) return 1;

      /* ... and then finally ids. */
        if      (port1.id < port2.id) return -1;
        else if (port1.id > port2.id) return 1;

        return 0;
    }
    case ERL_NIL_EXT: return 0;
    case ERL_LIST_EXT:
      i = (**e1 << 24) | ((*e1)[1] << 16) |((*e1)[2] << 8) | (*e1)[3];
      *e1 += 4;
      j = (**e2 << 24) | ((*e2)[1] << 16) |((*e2)[2] << 8) | (*e2)[3];
      *e2 += 4;
      if ( i == j && j == 0 ) return 0;
      min = (i < j) ? i : j;
      k = 0;
      while (1) {
	  if (k++ == min){
	      if (i == j) return 0;
	      if (i < j) return -1;
	      return 1;
	  }
	  if ((ret = compare_top_ext(e1 , e2)) == 0)
	      continue;
	  return ret;
      }
    case ERL_STRING_EXT:
      i = (**e1 << 8) | ((*e1)[1]);
      *e1 += 2;
      j = (**e2 << 8) | ((*e2)[1]);
      *e2 += 2;
      ret = cmpbytes(*e1, i, *e2, j);
      *e1 += i;
      *e2 += j;
      return ret;
    case ERL_SMALL_TUPLE_EXT:
      i = *(*e1)++; 	j = *(*e2)++;
      if (i < j) return -1;
      if (i > j ) return 1;
      while (i--) {
	if ((j = compare_top_ext(e1, e2))) return j;
      }
      return 0;
    case ERL_LARGE_TUPLE_EXT:
      i = (**e1 << 24) | ((*e1)[1]) << 16| ((*e1)[2]) << 8| ((*e1)[3]) ;	
      *e1 += 4;
      j = (**e2 << 24) | ((*e2)[1]) << 16| ((*e2)[2]) << 8| ((*e2)[3]) ;	
      *e2 += 4;
      if (i < j) return -1;
      if (i > j ) return 1;
      while (i--) {
	if ((j = compare_top_ext(e1, e2))) return j;
      }
      return 0;
    case ERL_FLOAT_EXT:
    case NEW_FLOAT_EXT:
      i = -1;
      if (ei_decode_double((char *) *e1, &i, &ff1) != 0)
        return -1;
      *e1 += i;
      j = -1;
      if (ei_decode_double((char *) *e2, &j, &ff2) != 0)
        return -1;
      *e2 += j;
      return cmp_floats(ff1,ff2);

    case ERL_BINARY_EXT:
      i = (**e1 << 24) | ((*e1)[1] << 16) |((*e1)[2] << 8) | (*e1)[3];
      *e1 += 4;
      j = (**e2 << 24) | ((*e2)[1] << 16) |((*e2)[2] << 8) | (*e2)[3];
      *e2 += 4;
      ret = cmpbytes(*e1, i , *e2 , j);
      *e1 += i; *e2 += j;
      return ret;

    case ERL_FUN_EXT:  /* FIXME: */
    case ERL_NEW_FUN_EXT:  /* FIXME: */
      return -1;

    default:
      return cmpbytes(*e1, 1, *e2, 1);

    } /* switch */
  
} /* cmp_exe2 */

/* Number compare */

static int cmp_floats(double f1, double f2)
{
#if defined(VXWORKS) && CPU == PPC860
      return erl_fp_compare((unsigned *) &f1, (unsigned *) &f2);
#else
      if (f1<f2) return -1;
      else if (f1>f2) return 1;
      else return 0;
#endif
}

static INLINE double to_float(long l) 
{
    double f;
#if defined(VXWORKS) && CPU == PPC860
    erl_long_to_fp(l, (unsigned *) &f);
#else
    f = l;
#endif
    return f;
}


static int cmp_small_big(unsigned char**e1, unsigned char **e2)
{
    int i1,i2;
    int t2;
    int n2;
    long l1;
    int res;

    erlang_big *b1,*b2;

    i1 = i2 = 0;
    if ( ei_decode_long((char *)*e1,&i1,&l1) < 0 ) return -1;
    
    ei_get_type((char *)*e2,&i2,&t2,&n2);
    
    /* any small will fit in two digits */
    if ( (b1 = ei_alloc_big(2)) == NULL ) return -1;
    if ( ei_small_to_big(l1,b1) < 0 ) {
        ei_free_big(b1);
        return -1;
    }
    
    if ( (b2 = ei_alloc_big(n2)) == NULL ) {
        ei_free_big(b1);
        return 1;
    }

    if ( ei_decode_big((char *)*e2,&i2,b2) < 0 ) {
        ei_free_big(b1);
        ei_free_big(b2);
        return 1;
    }
    
    res = ei_big_comp(b1,b2);
    
    ei_free_big(b1);
    ei_free_big(b2);

    *e1 += i1;
    *e2 += i2;

    return res;
}

static int cmp_small_float(unsigned char**e1, unsigned char **e2)
{
    int i1,i2;
    long l1;
    double f1,f2;

    /* small -> float -> float_comp */

    i1 = i2 = 0;
    if ( ei_decode_long((char *)*e1,&i1,&l1) < 0 ) return -1;
    if ( ei_decode_double((char *)*e2,&i2,&f2) < 0 ) return 1;
    
    f1 = to_float(l1);

    *e1 += i1;
    *e2 += i2;

    return cmp_floats(f1,f2);
}

static int cmp_float_big(unsigned char**e1, unsigned char **e2)
{
    int res;
    int i1,i2;
    int t2,n2;
    double f1,f2;
    erlang_big *b2;
    
    /* big -> float if overflow return big sign else float_comp */
    
    i1 = i2 = 0;
    if ( ei_decode_double((char *)*e1,&i1,&f1) < 0 ) return -1;
    
    if (ei_get_type((char *)*e2,&i2,&t2,&n2) < 0) return 1;
    if ((b2 = ei_alloc_big(n2)) == NULL) return 1;
    if (ei_decode_big((char *)*e2,&i2,b2) < 0) return 1;
    
    /* convert the big to float */
    if ( ei_big_to_double(b2,&f2) < 0 ) {
        /* exception look at the sign */
        res = b2->is_neg ? 1 : -1;
        ei_free_big(b2);
        return res;
    }
    
    ei_free_big(b2);

    *e1 += i1;
    *e2 += i2;

    return cmp_floats(f1,f2);
}

static int cmp_small_small(unsigned char**e1, unsigned char **e2)
{
    int i1,i2;
    long l1,l2;

    i1 = i2 = 0;
    if ( ei_decode_long((char *)*e1,&i1,&l1) < 0 ) {
        fprintf(stderr,"Failed to decode 1\r\n");
        return -1;
    }
    if ( ei_decode_long((char *)*e2,&i2,&l2) < 0 ) {
        fprintf(stderr,"Failed to decode 2\r\n");
        return 1;
    }
    
    *e1 += i1;
    *e2 += i2;
    
    if ( l1 < l2 ) return -1;
    else if ( l1 > l2 ) return 1;
    else return 0;
}

static int cmp_float_float(unsigned char**e1, unsigned char **e2)
{
    int i1,i2;
    double f1,f2;

    i1 = i2 = 0;
    if ( ei_decode_double((char *)*e1,&i1,&f1) < 0 ) return -1;
    if ( ei_decode_double((char *)*e2,&i2,&f2) < 0 ) return 1;
    
    *e1 += i1;
    *e2 += i2;
    
    return cmp_floats(f1,f2);
}

static int cmp_big_big(unsigned char**e1, unsigned char **e2)
{
    int res;
    int i1,i2;
    int t1,t2;
    int n1,n2;
    erlang_big *b1,*b2;

    i1 = i2 = 0;
    ei_get_type((char *)*e1,&i1,&t1,&n1);
    ei_get_type((char *)*e2,&i2,&t2,&n2);
    
    if ( (b1 = ei_alloc_big(n1)) == NULL) return -1;
    if ( (b2 = ei_alloc_big(n2)) == NULL) {
        ei_free_big(b1);
        return 1;
    }
    
    ei_decode_big((char *)*e1,&i1,b1);
    ei_decode_big((char *)*e2,&i2,b2);
    
    res = ei_big_comp(b1,b2);
    
    ei_free_big(b1);
    ei_free_big(b2);
    
    *e1 += i1;
    *e2 += i2;

    return res;
}

static int cmp_number(unsigned char**e1, unsigned char **e2)
{
    switch (CMP_NUM_CODE(**e1,**e2)) {

      case SMALL_BIG:
        /* fprintf(stderr,"compare small_big\r\n"); */
        return cmp_small_big(e1,e2);

      case BIG_SMALL:
        /* fprintf(stderr,"compare sbig_small\r\n"); */
        return -cmp_small_big(e2,e1);

      case SMALL_FLOAT:
        /* fprintf(stderr,"compare small_float\r\n"); */
        return cmp_small_float(e1,e2);
        
      case FLOAT_SMALL:
        /* fprintf(stderr,"compare float_small\r\n"); */
        return -cmp_small_float(e2,e1);

      case FLOAT_BIG:
        /* fprintf(stderr,"compare float_big\r\n"); */
        return cmp_float_big(e1,e2);

      case BIG_FLOAT:
        /* fprintf(stderr,"compare big_float\r\n"); */
        return -cmp_float_big(e2,e1);

      case SMALL_SMALL:
        /* fprintf(stderr,"compare small_small\r\n"); */
        return cmp_small_small(e1,e2);

      case FLOAT_FLOAT:
        /* fprintf(stderr,"compare float_float\r\n"); */
        return cmp_float_float(e1,e2);

      case BIG_BIG:
        /* fprintf(stderr,"compare big_big\r\n"); */
        return cmp_big_big(e1,e2);

      default:
        /* should never get here ... */
        /* fprintf(stderr,"compare standard\r\n"); */
        return cmp_exe2(e1,e2);
    }

}

/* 
 * If the arrays are of the same type, then we
 * have to do a real compare.
 */
/* 
 * COMPARE TWO encoded BYTE ARRAYS e1 and e2.
 * Return: -1 if e1 < e2
 *          0 if e1 == e2 
 *          1 if e2 > e1   
 */
static int compare_top_ext(unsigned char**e1, unsigned char **e2)
{
  if (**e1 == ERL_VERSION_MAGIC) (*e1)++;
  if (**e2 == ERL_VERSION_MAGIC) (*e2)++;

  if (cmp_array[**e1] < cmp_array[**e2]) return -1;
  if (cmp_array[**e1] > cmp_array[**e2]) return 1;
  
  if (IS_ERL_NUM(**e1)) 
      return cmp_number(e1,e2);

  if (cmp_array[**e1] == ERL_REF_CMP)
      return cmp_refs(e1, e2);

  return cmp_exe2(e1, e2);
}

int erl_compare_ext(unsigned char *e1, unsigned char *e2)
{
  return compare_top_ext(&e1, &e2); 
} /* erl_compare_ext */

#if defined(VXWORKS) && CPU == PPC860
/* FIXME we have no floating point but don't we have emulation?! */
int erl_fp_compare(unsigned *a, unsigned *b) 
{
    /* Big endian mode of powerPC, IEEE floating point. */
    unsigned a_split[4] = {a[0] >> 31,             /* Sign bit */
                           (a[0] >> 20) & 0x7FFU,  /* Exponent */
                           a[0] & 0xFFFFFU,        /* Mantissa MS bits */
                           a[1]};                  /* Mantissa LS bits */
    unsigned b_split[4] = {b[0] >> 31,
                           (b[0] >> 20) & 0x7FFU,
                           b[0] & 0xFFFFFU,
                           b[1]};
    int a_is_infinite, b_is_infinite;
    int res;


    /* Make -0 be +0 */
    if (a_split[1] == 0 && a_split[2] == 0 && a_split[3] == 0)
        a_split[0] = 0;
    if (b_split[1] == 0 && b_split[2] == 0 && b_split[3] == 0)
        b_split[0] = 0;
    /* Check for infinity */
    a_is_infinite = (a_split[1] == 0x7FFU && a_split[2] == 0 && 
                     a_split[3] == 0);
    b_is_infinite = (b_split[1] == 0x7FFU && b_split[2] == 0 && 
                     b_split[3] == 0);

    if (a_is_infinite && !b_is_infinite)
        return (a_split[0]) ? -1 : 1;
    if (b_is_infinite && !a_is_infinite)
        return (b_split[0]) ? 1 : -1;
    if (a_is_infinite && b_is_infinite)
        return b[0] - a[0]; 
    /* Check for indeterminate or nan, infinite is already handled, 
     so we only check the exponent. */
    if((a_split[1] == 0x7FFU) || (b_split[1] == 0x7FFU))
        return INT_MAX; /* Well, they are not equal anyway, 
                           abort() could be an alternative... */

    if (a_split[0] && !b_split[0])
        return -1;
    if (b_split[0] && !a_split[0])
        return 1;
    /* Compare */
    res = memcmp(a_split + 1, b_split + 1, 3 * sizeof(unsigned));
    /* Make -1, 0 or 1 */
    res = (!!res) * ((res < 0) ? -1 : 1); 
    /* Turn sign if negative values */
    if (a_split[0]) /* Both are negative */
        res = -1 * res;
    return res;
}

static void join(unsigned d_split[4], unsigned *d)
{
    d[0] = (d_split[0] << 31) |         /* Sign bit */
	((d_split[1] & 0x7FFU) << 20) | /* Exponent */
	(d_split[2] & 0xFFFFFU);        /* Mantissa MS bits */
    d[1] = d_split[3];                  /* Mantissa LS bits */
}

static int blength(unsigned long l)
{
    int i;
    for(i = 0; l; ++i)
	l >>= 1;
    return i;
}

static void erl_long_to_fp(long l, unsigned *d) 
{
    unsigned d_split[4];
    unsigned x;
    if (l < 0) {
	d_split[0] = 1;
	x = -l;
    } else {
	d_split[0] = 0;
	x = l;
    }

    if (!l) {
	memset(d_split,0,sizeof(d_split));
    } else {
	int len = blength(x);
	x <<= (33 - len);
	d_split[2] = (x >> 12);
	d_split[3] = (x << 20);
	d_split[1] = 1023 + len - 1;
    }
    join(d_split,d);
}

#endif


/* 
 * Checks if a term is a "string": a flat list of byte-sized integers.
 *
 * Returns: 0 if the term is not a string, otherwise the length is returned.
 */

static int is_string(ETERM* term)
{
    int len = 0;

    while (ERL_TYPE(term) == ERL_LIST) {
	ETERM* head = HEAD(term);

	if (!ERL_IS_INTEGER(head) || ((unsigned)head->uval.ival.i) > 255) {
	    return 0;
	}
	len++;
	term = TAIL(term);
    }

    if (ERL_IS_EMPTY_LIST(term)) {
	return len;
    }
    return 0;
}