76d864b117c777d28eedf1be66c016b1e4a300c6
[charm.git] / src / util / pup_xlater.C
1 /*
2 Pack/UnPack Library for UIUC Parallel Programming Lab
3 Orion Sky Lawlor, olawlor@uiuc.edu, 4/5/2000
4
5 This part of the pack/unpack library handles translating
6 between different binary representations for integers and floats.
7 All machines are assumed to be byte-oriented.
8
9 Currently supported are converting between 8,16,32,64, and 128-bit
10 integers, and swapping bytes between big and little integers and
11 big and little-endian IEEE 32- and 64-bit floats.
12
13 */
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include "pup.h"
18
19 //////////////////// MachineInfo utilities ///////////////////
20
21 //This 4-byte sequence identifies a PPL machineInfo structure.
22 static const unsigned char machInfo_magic[4]={0x10,0xea,0xbd,0xf9};
23
24 //Return true if our magic number is valid.
25 CmiBool PUP::machineInfo::valid(void) const
26 {
27         for (int i=0;i<4;i++)
28                 if (magic[i]!=machInfo_magic[i])
29                         return CmiFalse;
30         return CmiTrue;
31 }
32
33 //Return true if we differ from the current (running) machine.
34 CmiBool PUP::machineInfo::needsConversion(void) const
35 {
36         const machineInfo &m=current();
37         if (intFormat==m.intFormat && floatFormat==m.floatFormat &&
38             intBytes[0]==m.intBytes[0] && intBytes[1]==m.intBytes[1] &&
39             intBytes[2]==m.intBytes[2] && intBytes[3]==m.intBytes[3] && 
40             floatBytes==m.floatBytes && doubleBytes==m.doubleBytes && 
41             boolBytes==m.boolBytes && pointerBytes==m.pointerBytes
42            )
43                 return CmiFalse;//No conversion needed
44         else 
45                 return CmiTrue;//Some differences-- convert
46 }
47
48 ////////// For getting info. about the current machine /////////
49 static int getIntFormat(void)
50 {
51         int test=0x1c;
52         unsigned char *c=(unsigned char *)&test;
53         if (c[sizeof(int)-1]==0x1c) 
54                 //Macintosh and most workstations are big-endian
55                 return 0;//Big-endian machine
56         if (c[0]==0x1c) 
57                 //Intel x86 PC's, and DEC VAX are little-endian
58                 return 1;//Little-endian machine
59         return 99;//Unknown integer type
60 }
61 /*Known values for this routine come from this (compressed) program:
62 main() {double d=-9.5; unsigned char *c=(unsigned char *)&d;
63 int i; for (i=0;i<sizeof(double);i++) printf("c[%d]==0x%02x && ",i,c[i]); }
64 */
65 int getFloatFormat(void)
66 {
67         float ftest=-9.5;//Float test value
68         double dtest=-9.5;//Double test value
69         
70         //Find the 8-byte floating-point type
71         unsigned char *c;
72             if (sizeof(double)==8) c=(unsigned char *)&dtest;
73         else if (sizeof(float)==8) c=(unsigned char *)&ftest;
74         else return 98;//Unknown floating-point sizes
75         
76         if (c[0]==0xc0 && c[1]==0x23 && c[2]==0x00 && c[3]==0x00) 
77                 return 0;//Big-endian IEEE machine (e.g., Mac, Sun, SGI)
78         if (c[4]==0x00 && c[5]==0x00 && c[6]==0x23 && c[7]==0xc0) 
79                 return 1;//Little-endian IEEE machine (e.g., Intel)
80         if (c[0]==0xC0 && c[1]==0x04 && c[2]==0x98 && c[3]==0x00)
81                 return 50;//Cray Y-MP (unsupported)
82         return 99;//Unknown machine type
83 }
84
85 const PUP::machineInfo &PUP::machineInfo::current(void)
86 {
87         static machineInfo *m=NULL;
88         if (m==NULL) 
89         {//Allocate, initialize, and return m
90                 m=new machineInfo();
91                 for (int i=0;i<4;i++)
92                         m->magic[i]=machInfo_magic[i];
93                 m->version=0;
94                 m->intBytes[0]=sizeof(char);
95                 m->intBytes[1]=sizeof(short);
96                 m->intBytes[2]=sizeof(int);
97                 m->intBytes[3]=sizeof(long);
98 #if CMK___int128_DEFINED
99                 m->intBytes[4]=sizeof(__int128);
100 #endif
101                 m->intFormat=getIntFormat();
102                 m->floatBytes=sizeof(float);
103                 m->doubleBytes=sizeof(double);
104                 m->floatFormat=getFloatFormat();
105                 m->boolBytes=sizeof(CmiBool);
106                 m->pointerBytes=sizeof(void*);
107                 m->padding[0]=0;
108         }
109         return *m;
110 }
111
112 ////////////////////////// Conversion Functions ///////////////////////////
113 typedef unsigned char myByte;
114
115 //Do nothing to the given bytes (the "null conversion")
116 static void cvt_null(int N,const myByte *in,myByte *out,int nElem) {}
117
118 //Swap the order of each N-byte chunk in the given array (in can equal out)
119 static void cvt_swap(int N,const myByte *in,myByte *out,int nElem)
120 {
121         int i;
122         for (i=0;i<nElem;i++)
123         {
124                 const myByte *s=&in[N*i];
125                 myByte t,*d=&out[N*i];
126                 for (int j=N/2-1;j>=0;j--)
127                         {t=s[j];d[j]=s[N-j-1];d[N-j-1]=t;}
128         }
129 }
130 /*******************************************************
131 Convert N-byte boolean to machine boolean.
132 */
133 static void cvt_bool(int N,const myByte *in,myByte *out,int nElem)
134 {
135         int i;for (i=nElem-1;i>=0;i--)
136         {
137                 const myByte *s=&in[N*i];
138                 CmiBool ret=CmiFalse;
139                 int j;for (j=0;j<N;j++)
140                         if (s[j]!=0) //Some bit is set
141                                 ret=CmiTrue;
142                 ((CmiBool *)(out))[i]=ret;
143         }
144 }
145
146 /*******************************************************
147 Convert N-byte big or little endian integers to 
148 native char, short, or long signed or unsigned. 
149 Since this is so many functions, we define them 
150 with the preprocessor.
151
152 Values too large to be represented will be garbage 
153 (keeping only the low-order bits).
154 */
155
156 /// These defines actually provide the conversion function bodies
157 #define def_cvtFunc(bigName,bigIdx,nameT,rT,uT) \
158 static void cvt##bigName##_to##nameT(int N,const myByte *in,myByte *out,int nElem) \
159 { \
160         int i;for (i=0;i<nElem;i++)\
161         {\
162                 const myByte *s=&in[N*i];\
163                 rT ret=0;\
164                 int j;\
165                 for (j=0;j<N-1;j++) \
166                         ret|=((uT)s[bigIdx])<<(8*j);\
167                 ret|=((rT)s[bigIdx])<<(8*j);\
168                 ((rT *)(out))[i]=ret;\
169         }\
170 }
171 #define def_cvtBig_toT(T)  def_cvtFunc(Big,N-j-1,T    ,T         ,unsigned T)
172 #define def_cvtBig_touT(T) def_cvtFunc(Big,N-j-1,u##T ,unsigned T,unsigned T)
173 #define def_cvtLil_toT(T)  def_cvtFunc(Lil,j    ,T    ,T         ,unsigned T)
174 #define def_cvtLil_touT(T) def_cvtFunc(Lil,j    ,u##T ,unsigned T,unsigned T)
175
176 #define def_cvtTypes(cvtNT) \
177 cvtNT(char)  cvtNT(short)  cvtNT(int)  cvtNT(long)
178
179 def_cvtTypes(def_cvtLil_toT)  //the lil conversion functions
180 def_cvtTypes(def_cvtLil_touT) //the lil unsigned conversion functions
181 def_cvtTypes(def_cvtBig_toT)  //the big conversion functions
182 def_cvtTypes(def_cvtBig_touT) //the big unsigned conversion functions
183
184
185 /// These defines are used to initialize the conversion function array below
186 #define arr_cvtBig_toT(T)  cvtBig_to##T
187 #define arr_cvtBig_touT(T) cvtBig_tou##T
188 #define arr_cvtLil_toT(T)  cvtLil_to##T
189 #define arr_cvtLil_touT(T) cvtLil_tou##T
190
191 #define arr_cvtTypes(cvtNT) \
192   {cvtNT(char), cvtNT(short), cvtNT(int), cvtNT(long)}
193
194 typedef void (*dataConverterFn)(int N,const myByte *in,myByte *out,int nElem);
195
196 const static dataConverterFn cvt_intTable
197         [2]//Indexed by source endian-ness (big--0, little-- 1)
198         [2]//Indexed by signed-ness (signed--0, unsigned-- 1)
199         [4]//Index by dest type (0-- char, 1-- short, 2-- int, 3-- long)
200 ={
201 { arr_cvtTypes(arr_cvtBig_toT),  //the big conversion functions
202   arr_cvtTypes(arr_cvtBig_touT) }, //the big unsigned conversion functions
203 { arr_cvtTypes(arr_cvtLil_toT),  //the lil conversion functions
204   arr_cvtTypes(arr_cvtLil_touT) } //the lil unsigned conversion functions
205 };
206
207 /*Set an appropriate conversion function for the given
208 number of source integer bytes to the given integer type index.
209 */
210 void PUP::xlater::setConverterInt(const machineInfo &src,const machineInfo &cur,
211         int isUnsigned,int intType,dataType dest)
212 {
213         if (src.intFormat==cur.intFormat && src.intBytes[intType]==cur.intBytes[intType])
214                 convertFn[dest]=cvt_null;//Same format and size-- no conversion needed
215         else 
216                 convertFn[dest]=cvt_intTable[src.intFormat][isUnsigned][intType];
217         convertSize[dest]=src.intBytes[intType];
218 }
219
220 //Return the appropriate floating-point conversion routine 
221 static dataConverterFn converterFloat(
222         const PUP::machineInfo &src,const PUP::machineInfo &cur,
223         int srcSize,int curSize)
224 {
225         if (src.floatFormat==cur.floatFormat && srcSize==curSize)
226                 return cvt_null;//No conversion needed
227         else {
228                 if ((src.floatFormat==1 && cur.floatFormat==0)
229                   ||(src.floatFormat==0 && cur.floatFormat==1))
230                 {//Endian-ness difference only-- just swap bytes
231                         if (srcSize==4 && curSize==4)
232                                 return cvt_swap;
233                         else if (srcSize==8 && curSize==8)
234                                 return cvt_swap;
235                 }
236         }
237         fprintf(stderr,__FILE__" Non-convertible float sizes %d and %d\n",srcSize,curSize);
238         abort();
239         return NULL;//<- for whining compilers
240 }
241
242 /*Constructor (builds conversionFn table)*/
243 PUP::xlater::xlater(const PUP::machineInfo &src,PUP::er &fromData)
244         :wrap_er(fromData)
245 {
246         const machineInfo &cur=PUP::machineInfo::current();
247         if (src.intFormat>1) abort();//Unknown integer format
248         //Set up table for converting integral types
249         setConverterInt(src,cur,0,0,Tchar);
250         setConverterInt(src,cur,0,1,Tshort);
251         setConverterInt(src,cur,0,2,Tint);
252         setConverterInt(src,cur,0,3,Tlong);
253         setConverterInt(src,cur,1,0,Tuchar);
254         setConverterInt(src,cur,1,1,Tushort);
255         setConverterInt(src,cur,1,2,Tuint);
256         setConverterInt(src,cur,1,3,Tulong);
257         if (src.intFormat==cur.intFormat) //At worst, need to swap 8-byte integers:
258                 convertFn[Tlonglong]=convertFn[Tulonglong]=cvt_null;
259         else
260                 convertFn[Tlonglong]=convertFn[Tulonglong]=cvt_swap;
261 #if CMK___int128_DEFINED
262         setConverterInt(src,cur,0,4,Tint128);
263         setConverterInt(src,cur,1,4,Tuint128);
264 #endif
265         convertFn[Tfloat]=converterFloat(src,cur,src.floatBytes,cur.floatBytes);
266         convertFn[Tdouble]=converterFloat(src,cur,src.doubleBytes,cur.doubleBytes);
267         convertFn[Tlongdouble]=cvt_null; //<- a lie, but no better alternative
268         
269         if (src.boolBytes!=cur.boolBytes)
270                 convertFn[Tbool]=cvt_bool;
271         else
272                 convertFn[Tbool]=cvt_null;
273         
274         convertFn[Tbyte]=cvt_null;//Bytes are never converted at all
275         setConverterInt(src,cur,0,2,Tsync);
276         convertFn[Tpointer]=cvt_null; //<- a lie, but pointers should never be converted across machines
277         
278         //Finish out the size table (integer portion is done by setConverterInt)
279 #ifdef CMK_PUP_LONG_LONG
280         convertSize[Tlonglong]=convertSize[Tlonglong]=sizeof(CMK_PUP_LONG_LONG);
281 #else
282         convertSize[Tlonglong]=convertSize[Tlonglong]=8;
283 #endif
284         convertSize[Tfloat]=src.floatBytes;
285         convertSize[Tdouble]=src.doubleBytes;
286 #if CMK_LONG_DOUBLE_DEFINED
287         convertSize[Tlongdouble]=sizeof(long double);
288 #else
289         convertSize[Tlongdouble]=12; //<- again, a lie.  Need machineInfo.longdoubleSize!
290 #endif
291         convertSize[Tbool]=src.boolBytes;
292         convertSize[Tbyte]=1;//Byte always takes one byte of storage
293         convertSize[Tpointer]=src.pointerBytes;
294 }
295
296 //Generic bottleneck: unpack n items of size itemSize from p.
297 void PUP::xlater::bytes(void *ptr,int n,size_t itemSize,dataType t)
298 {
299         if (convertSize[t]==itemSize)
300         {//Do conversion in-place
301                 p.bytes(ptr,n,itemSize,t);
302                 convertFn[t](itemSize,(const myByte *)ptr,(myByte *)ptr,n);//Convert in-place
303         }
304         else 
305         {//Read into temporary buffer, unpack, and then convert
306                 void *buf=(void *)malloc(convertSize[t]*n);
307                 p.bytes(buf,n,convertSize[t],t);
308                 convertFn[t](convertSize[t],(const myByte *)buf,(myByte *)ptr,n);
309                 free(buf);
310         }
311 }
312