1/*
2*******************************************************************************
3*
4*   Copyright (C) 2003, International Business Machines
5*   Corporation and others.  All Rights Reserved.
6*
7*******************************************************************************
8*   file name:  udataswp.c
9*   encoding:   US-ASCII
10*   tab size:   8 (not used)
11*   indentation:4
12*
13*   created on: 2003jun05
14*   created by: Markus W. Scherer
15*
16*   Definitions for ICU data transformations for different platforms,
17*   changing between big- and little-endian data and/or between
18*   charset families (ASCII<->EBCDIC).
19*/
20
21#include <stdarg.h>
22#include "unicode/utypes.h"
23#include "unicode/udata.h" /* UDataInfo */
24#include "ucmndata.h" /* DataHeader */
25#include "cmemory.h"
26#include "udataswp.h"
27
28/* swapping primitives ------------------------------------------------------ */
29
30static int32_t U_CALLCONV
31uprv_swapArray16(const UDataSwapper *ds,
32                 const void *inData, int32_t length, void *outData,
33                 UErrorCode *pErrorCode) {
34    const uint16_t *p;
35    uint16_t *q;
36    int32_t count;
37    uint16_t x;
38
39    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
40        return 0;
41    }
42    if(ds==NULL || inData==NULL || length<0 || (length&1)!=0 || outData==NULL) {
43        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
44        return 0;
45    }
46
47    /* setup and swapping */
48    p=(const uint16_t *)inData;
49    q=(uint16_t *)outData;
50    count=length/2;
51    while(count>0) {
52        x=*p++;
53        *q++=(uint16_t)((x<<8)|(x>>8));
54        --count;
55    }
56
57    return length;
58}
59
60static int32_t U_CALLCONV
61uprv_copyArray16(const UDataSwapper *ds,
62                 const void *inData, int32_t length, void *outData,
63                 UErrorCode *pErrorCode) {
64    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
65        return 0;
66    }
67    if(ds==NULL || inData==NULL || length<0 || (length&1)!=0 || outData==NULL) {
68        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
69        return 0;
70    }
71
72    if(length>0 && inData!=outData) {
73        uprv_memcpy(outData, inData, length);
74    }
75    return length;
76}
77
78static int32_t U_CALLCONV
79uprv_swapArray32(const UDataSwapper *ds,
80                 const void *inData, int32_t length, void *outData,
81                 UErrorCode *pErrorCode) {
82    const uint32_t *p;
83    uint32_t *q;
84    int32_t count;
85    uint32_t x;
86
87    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
88        return 0;
89    }
90    if(ds==NULL || inData==NULL || length<0 || (length&3)!=0 || outData==NULL) {
91        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
92        return 0;
93    }
94
95    /* setup and swapping */
96    p=(const uint32_t *)inData;
97    q=(uint32_t *)outData;
98    count=length/4;
99    while(count>0) {
100        x=*p++;
101        *q++=(uint32_t)((x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24));
102        --count;
103    }
104
105    return length;
106}
107
108static int32_t U_CALLCONV
109uprv_copyArray32(const UDataSwapper *ds,
110                 const void *inData, int32_t length, void *outData,
111                 UErrorCode *pErrorCode) {
112    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
113        return 0;
114    }
115    if(ds==NULL || inData==NULL || length<0 || (length&3)!=0 || outData==NULL) {
116        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
117        return 0;
118    }
119
120    if(length>0 && inData!=outData) {
121        uprv_memcpy(outData, inData, length);
122    }
123    return length;
124}
125
126static uint16_t U_CALLCONV
127uprv_readSwapUInt16(uint16_t x) {
128    return (uint16_t)((x<<8)|(x>>8));
129}
130
131static uint16_t U_CALLCONV
132uprv_readDirectUInt16(uint16_t x) {
133    return x;
134}
135
136static uint32_t U_CALLCONV
137uprv_readSwapUInt32(uint32_t x) {
138    return (uint32_t)((x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24));
139}
140
141static uint32_t U_CALLCONV
142uprv_readDirectUInt32(uint32_t x) {
143    return x;
144}
145
146static void U_CALLCONV
147uprv_writeSwapUInt16(uint16_t *p, uint16_t x) {
148    *p=(uint16_t)((x<<8)|(x>>8));
149}
150
151static void U_CALLCONV
152uprv_writeDirectUInt16(uint16_t *p, uint16_t x) {
153    *p=x;
154}
155
156static void U_CALLCONV
157uprv_writeSwapUInt32(uint32_t *p, uint32_t x) {
158    *p=(uint32_t)((x<<24)|((x<<8)&0xff0000)|((x>>8)&0xff00)|(x>>24));
159}
160
161static void U_CALLCONV
162uprv_writeDirectUInt32(uint32_t *p, uint32_t x) {
163    *p=x;
164}
165
166U_CAPI int16_t U_EXPORT2
167udata_readInt16(const UDataSwapper *ds, int16_t x) {
168    return (int16_t)ds->readUInt16((uint16_t)x);
169}
170
171U_CAPI int32_t U_EXPORT2
172udata_readInt32(const UDataSwapper *ds, int32_t x) {
173    return (int32_t)ds->readUInt32((uint32_t)x);
174}
175
176/**
177 * Swap a block of invariant, NUL-terminated strings, but not padding
178 * bytes after the last string.
179 * @internal
180 */
181U_CAPI int32_t U_EXPORT2
182udata_swapInvStringBlock(const UDataSwapper *ds,
183                         const void *inData, int32_t length, void *outData,
184                         UErrorCode *pErrorCode) {
185    const char *inChars;
186    int32_t stringsLength;
187
188    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
189        return 0;
190    }
191    if(ds==NULL || inData==NULL || length<0 || (length>0 && outData==NULL)) {
192        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
193        return 0;
194    }
195
196    /* reduce the strings length to not include bytes after the last NUL */
197    inChars=(const char *)inData;
198    stringsLength=length;
199    while(stringsLength>0 && inChars[stringsLength-1]!=0) {
200        --stringsLength;
201    }
202
203    /* swap up to the last NUL */
204    ds->swapInvChars(ds, inData, stringsLength, outData, pErrorCode);
205
206    /* copy the bytes after the last NUL */
207    if(inData!=outData && length>stringsLength) {
208        uprv_memcpy((char *)outData+stringsLength, inChars+stringsLength, length-stringsLength);
209    }
210
211    /* return the length including padding bytes */
212    if(U_SUCCESS(*pErrorCode)) {
213        return length;
214    } else {
215        return 0;
216    }
217}
218
219U_CAPI void U_EXPORT2
220udata_printError(const UDataSwapper *ds,
221                 const char *fmt,
222                 ...) {
223    va_list args;
224
225    if(ds->printError!=NULL) {
226        va_start(args, fmt);
227        ds->printError(ds->printErrorContext, fmt, args);
228        va_end(args);
229    }
230}
231
232/* swap a data header ------------------------------------------------------- */
233
234U_CAPI int32_t U_EXPORT2
235udata_swapDataHeader(const UDataSwapper *ds,
236                     const void *inData, int32_t length, void *outData,
237                     UErrorCode *pErrorCode) {
238    const DataHeader *pHeader;
239    uint16_t headerSize, infoSize;
240
241    /* argument checking */
242    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
243        return 0;
244    }
245    if(ds==NULL || inData==NULL || length<-1 || (length>0 && outData==NULL)) {
246        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
247        return 0;
248    }
249
250    /* check minimum length and magic bytes */
251    pHeader=(const DataHeader *)inData;
252    if( (length>=0 && length<sizeof(DataHeader)) ||
253        pHeader->dataHeader.magic1!=0xda ||
254        pHeader->dataHeader.magic2!=0x27 ||
255        pHeader->info.sizeofUChar!=2
256    ) {
257        udata_printError(ds, "udata_swapDataHeader(): initial bytes do not look like ICU data\n");
258        *pErrorCode=U_UNSUPPORTED_ERROR;
259        return 0;
260    }
261
262    headerSize=ds->readUInt16(pHeader->dataHeader.headerSize);
263    infoSize=ds->readUInt16(pHeader->info.size);
264
265    if( headerSize<sizeof(DataHeader) ||
266        infoSize<sizeof(UDataInfo) ||
267        headerSize<(sizeof(pHeader->dataHeader)+infoSize) ||
268        (length>=0 && length<headerSize)
269    ) {
270        udata_printError(ds, "udata_swapDataHeader(): header size mismatch - headerSize %d infoSize %d length %d\n",
271                         headerSize, infoSize, length);
272        *pErrorCode=U_INDEX_OUTOFBOUNDS_ERROR;
273        return 0;
274    }
275
276    if(length>0) {
277        DataHeader *outHeader;
278        const char *s;
279        int32_t maxLength;
280
281        /* Most of the fields are just bytes and need no swapping. */
282        if(inData!=outData) {
283            uprv_memcpy(outData, inData, headerSize);
284        }
285        outHeader=(DataHeader *)outData;
286
287        outHeader->info.isBigEndian = ds->outIsBigEndian;
288        outHeader->info.charsetFamily = ds->outCharset;
289
290        /* swap headerSize */
291        ds->swapArray16(ds, &pHeader->dataHeader.headerSize, 2, &outHeader->dataHeader.headerSize, pErrorCode);
292
293        /* swap UDataInfo size and reservedWord */
294        ds->swapArray16(ds, &pHeader->info.size, 4, &outHeader->info.size, pErrorCode);
295
296        /* swap copyright statement after the UDataInfo */
297        infoSize+=sizeof(pHeader->dataHeader);
298        s=(const char *)inData+infoSize;
299        maxLength=headerSize-infoSize;
300        /* get the length of the string */
301        for(length=0; length<maxLength && s[length]!=0; ++length) {}
302        /* swap the string contents */
303        ds->swapInvChars(ds, s, length, (char *)outData+infoSize, pErrorCode);
304    }
305
306    return headerSize;
307}
308
309/* API functions ------------------------------------------------------------ */
310
311U_CAPI UDataSwapper * U_EXPORT2
312udata_openSwapper(UBool inIsBigEndian, uint8_t inCharset,
313                  UBool outIsBigEndian, uint8_t outCharset,
314                  UErrorCode *pErrorCode) {
315    UDataSwapper *swapper;
316
317    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
318        return NULL;
319    }
320    if(inCharset>U_EBCDIC_FAMILY || outCharset>U_EBCDIC_FAMILY) {
321        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
322        return NULL;
323    }
324
325    /* allocate the swapper */
326    swapper=uprv_malloc(sizeof(UDataSwapper));
327    if(swapper==NULL) {
328        *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
329        return NULL;
330    }
331    uprv_memset(swapper, 0, sizeof(UDataSwapper));
332
333    /* set values and functions pointers according to in/out parameters */
334    swapper->inIsBigEndian=inIsBigEndian;
335    swapper->inCharset=inCharset;
336    swapper->outIsBigEndian=outIsBigEndian;
337    swapper->outCharset=outCharset;
338
339    swapper->readUInt16= inIsBigEndian==U_IS_BIG_ENDIAN ? uprv_readDirectUInt16 : uprv_readSwapUInt16;
340    swapper->readUInt32= inIsBigEndian==U_IS_BIG_ENDIAN ? uprv_readDirectUInt32 : uprv_readSwapUInt32;
341
342    swapper->writeUInt16= outIsBigEndian==U_IS_BIG_ENDIAN ? uprv_writeDirectUInt16 : uprv_writeSwapUInt16;
343    swapper->writeUInt32= outIsBigEndian==U_IS_BIG_ENDIAN ? uprv_writeDirectUInt32 : uprv_writeSwapUInt32;
344
345    swapper->compareInvChars= outCharset==U_ASCII_FAMILY ? uprv_compareInvAscii : uprv_compareInvEbcdic;
346
347    swapper->swapArray16= inIsBigEndian==outIsBigEndian ? uprv_copyArray16 : uprv_swapArray16;
348    swapper->swapArray32= inIsBigEndian==outIsBigEndian ? uprv_copyArray32 : uprv_swapArray32;
349
350    if(inCharset==U_ASCII_FAMILY) {
351        swapper->swapInvChars= outCharset==U_ASCII_FAMILY ? uprv_copyAscii : uprv_ebcdicFromAscii;
352    } else /* U_EBCDIC_FAMILY */ {
353        swapper->swapInvChars= outCharset==U_EBCDIC_FAMILY ? uprv_copyEbcdic : uprv_asciiFromEbcdic;
354    }
355
356    return swapper;
357}
358
359U_CAPI UDataSwapper * U_EXPORT2
360udata_openSwapperForInputData(const void *data, int32_t length,
361                              UBool outIsBigEndian, uint8_t outCharset,
362                              UErrorCode *pErrorCode) {
363    const DataHeader *pHeader;
364    uint16_t headerSize, infoSize;
365    UBool inIsBigEndian;
366    int8_t inCharset;
367
368    if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) {
369        return NULL;
370    }
371    if( data==NULL ||
372        (length>=0 && length<sizeof(DataHeader)) ||
373        outCharset>U_EBCDIC_FAMILY
374    ) {
375        *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
376        return NULL;
377    }
378
379    pHeader=(const DataHeader *)data;
380    if( (length>=0 && length<sizeof(DataHeader)) ||
381        pHeader->dataHeader.magic1!=0xda ||
382        pHeader->dataHeader.magic2!=0x27 ||
383        pHeader->info.sizeofUChar!=2
384    ) {
385        *pErrorCode=U_UNSUPPORTED_ERROR;
386        return 0;
387    }
388
389    inIsBigEndian=(UBool)pHeader->info.isBigEndian;
390    inCharset=pHeader->info.charsetFamily;
391
392    if(inIsBigEndian==U_IS_BIG_ENDIAN) {
393        headerSize=pHeader->dataHeader.headerSize;
394        infoSize=pHeader->info.size;
395    } else {
396        headerSize=uprv_readSwapUInt16(pHeader->dataHeader.headerSize);
397        infoSize=uprv_readSwapUInt16(pHeader->info.size);
398    }
399
400    if( headerSize<sizeof(DataHeader) ||
401        infoSize<sizeof(UDataInfo) ||
402        headerSize<(sizeof(pHeader->dataHeader)+infoSize) ||
403        (length>=0 && length<headerSize)
404    ) {
405        *pErrorCode=U_UNSUPPORTED_ERROR;
406        return 0;
407    }
408
409    return udata_openSwapper(inIsBigEndian, inCharset, outIsBigEndian, outCharset, pErrorCode);
410}
411
412U_CAPI void U_EXPORT2
413udata_closeSwapper(UDataSwapper *ds) {
414    uprv_free(ds);
415}
416