1/*
2 * Copyright (C) 2005 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <utils/Debug.h>
18
19#include <utils/misc.h>
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <ctype.h>
24
25namespace android {
26
27// ---------------------------------------------------------------------
28
29static const char indentStr[] =
30"                                                                            "
31"                                                                            ";
32
33const char* stringForIndent(int32_t indentLevel)
34{
35    ssize_t off = sizeof(indentStr)-1-(indentLevel*2);
36    return indentStr + (off < 0 ? 0 : off);
37}
38
39// ---------------------------------------------------------------------
40
41static void defaultPrintFunc(void* cookie, const char* txt)
42{
43    printf("%s", txt);
44}
45
46// ---------------------------------------------------------------------
47
48static inline int isident(int c)
49{
50    return isalnum(c) || c == '_';
51}
52
53static inline bool isasciitype(char c)
54{
55    if( c >= ' ' && c < 127 && c != '\'' && c != '\\' ) return true;
56    return false;
57}
58
59static inline char makehexdigit(uint32_t val)
60{
61    return "0123456789abcdef"[val&0xF];
62}
63
64static char* appendhexnum(uint32_t val, char* out)
65{
66    for( int32_t i=28; i>=0; i-=4 ) {
67        *out++ = makehexdigit( val>>i );
68    }
69    *out = 0;
70    return out;
71}
72
73static inline char makeupperhexdigit(uint32_t val)
74{
75    return "0123456789ABCDEF"[val&0xF];
76}
77
78static char* appendupperhexnum(uint32_t val, char* out)
79{
80    for( int32_t i=28; i>=0; i-=4 ) {
81        *out++ = makeupperhexdigit( val>>i );
82    }
83    *out = 0;
84    return out;
85}
86
87static char* appendcharornum(char c, char* out, bool skipzero = true)
88{
89    if (skipzero && c == 0) return out;
90
91    if (isasciitype(c)) {
92        *out++ = c;
93        return out;
94    }
95
96    *out++ = '\\';
97    *out++ = 'x';
98    *out++ = makehexdigit(c>>4);
99    *out++ = makehexdigit(c);
100    return out;
101}
102
103static char* typetostring(uint32_t type, char* out,
104                          bool fullContext = true,
105                          bool strict = false)
106{
107    char* pos = out;
108    char c[4];
109    c[0] = (char)((type>>24)&0xFF);
110    c[1] = (char)((type>>16)&0xFF);
111    c[2] = (char)((type>>8)&0xFF);
112    c[3] = (char)(type&0xFF);
113    bool valid;
114    if( !strict ) {
115        // now even less strict!
116        // valid = isasciitype(c[3]);
117        valid = true;
118        int32_t i = 0;
119        bool zero = true;
120        while (valid && i<3) {
121            if (c[i] == 0) {
122                if (!zero) valid = false;
123            } else {
124                zero = false;
125                //if (!isasciitype(c[i])) valid = false;
126            }
127            i++;
128        }
129        // if all zeros, not a valid type code.
130        if (zero) valid = false;
131    } else {
132        valid = isident(c[3]) ? true : false;
133        int32_t i = 0;
134        bool zero = true;
135        while (valid && i<3) {
136            if (c[i] == 0) {
137                if (!zero) valid = false;
138            } else {
139                zero = false;
140                if (!isident(c[i])) valid = false;
141            }
142            i++;
143        }
144    }
145    if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) {
146        if( fullContext ) *pos++ = '\'';
147        pos = appendcharornum(c[0], pos);
148        pos = appendcharornum(c[1], pos);
149        pos = appendcharornum(c[2], pos);
150        pos = appendcharornum(c[3], pos);
151        if( fullContext ) *pos++ = '\'';
152        *pos = 0;
153        return pos;
154    }
155
156    if( fullContext ) {
157        *pos++ = '0';
158        *pos++ = 'x';
159    }
160    return appendhexnum(type, pos);
161}
162
163void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie)
164{
165    char buffer[32];
166    char* end = typetostring(typeCode, buffer);
167    *end = 0;
168    func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer);
169}
170
171void printHexData(int32_t indent, const void *buf, size_t length,
172    size_t bytesPerLine, int32_t singleLineBytesCutoff,
173    size_t alignment, bool cStyle,
174    debugPrintFunc func, void* cookie)
175{
176    if (alignment == 0) {
177        if (bytesPerLine >= 16) alignment = 4;
178        else if (bytesPerLine >= 8) alignment = 2;
179        else alignment = 1;
180    }
181    if (func == NULL) func = defaultPrintFunc;
182
183    size_t offset;
184
185    unsigned char *pos = (unsigned char *)buf;
186
187    if (pos == NULL) {
188        if (singleLineBytesCutoff < 0) func(cookie, "\n");
189        func(cookie, "(NULL)");
190        return;
191    }
192
193    if (length == 0) {
194        if (singleLineBytesCutoff < 0) func(cookie, "\n");
195        func(cookie, "(empty)");
196        return;
197    }
198
199    if ((int32_t)length < 0) {
200        if (singleLineBytesCutoff < 0) func(cookie, "\n");
201        char buf[64];
202        sprintf(buf, "(bad length: %d)", length);
203        func(cookie, buf);
204        return;
205    }
206
207    char buffer[256];
208    static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1);
209
210    if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine;
211
212    const bool oneLine = (int32_t)length <= singleLineBytesCutoff;
213    bool newLine = false;
214    if (cStyle) {
215        indent++;
216        func(cookie, "{\n");
217        newLine = true;
218    } else if (!oneLine) {
219        func(cookie, "\n");
220        newLine = true;
221    }
222
223    for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) {
224        long remain = length;
225
226        char* c = buffer;
227        if (!oneLine && !cStyle) {
228            sprintf(c, "0x%08x: ", (int)offset);
229            c += 12;
230        }
231
232        size_t index;
233        size_t word;
234
235        for (word = 0; word < bytesPerLine; ) {
236
237#ifdef HAVE_LITTLE_ENDIAN
238            const size_t startIndex = word+(alignment-(alignment?1:0));
239            const ssize_t dir = -1;
240#else
241            const size_t startIndex = word;
242            const ssize_t dir = 1;
243#endif
244
245            for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) {
246
247                if (!cStyle) {
248                    if (index == 0 && word > 0 && alignment > 0) {
249                        *c++ = ' ';
250                    }
251
252                    if (remain-- > 0) {
253                        const unsigned char val = *(pos+startIndex+(index*dir));
254                        *c++ = makehexdigit(val>>4);
255                        *c++ = makehexdigit(val);
256                    } else if (!oneLine) {
257                        *c++ = ' ';
258                        *c++ = ' ';
259                    }
260                } else {
261                    if (remain > 0) {
262                        if (index == 0 && word > 0) {
263                            *c++ = ',';
264                            *c++ = ' ';
265                        }
266                        if (index == 0) {
267                            *c++ = '0';
268                            *c++ = 'x';
269                        }
270                        const unsigned char val = *(pos+startIndex+(index*dir));
271                        *c++ = makehexdigit(val>>4);
272                        *c++ = makehexdigit(val);
273                        remain--;
274                    }
275                }
276            }
277
278            word += index;
279        }
280
281        if (!cStyle) {
282            remain = length;
283            *c++ = ' ';
284            *c++ = '\'';
285            for (index = 0; index < bytesPerLine; index++) {
286
287                if (remain-- > 0) {
288                    const unsigned char val = pos[index];
289                    *c++ = (val >= ' ' && val < 127) ? val : '.';
290                } else if (!oneLine) {
291                    *c++ = ' ';
292                }
293            }
294
295            *c++ = '\'';
296            if (length > bytesPerLine) *c++ = '\n';
297        } else {
298            if (remain > 0) *c++ = ',';
299            *c++ = '\n';
300        }
301
302        if (newLine && indent) func(cookie, stringForIndent(indent));
303        *c = 0;
304        func(cookie, buffer);
305        newLine = true;
306
307        if (length <= bytesPerLine) break;
308        length -= bytesPerLine;
309    }
310
311    if (cStyle) {
312        if (indent > 0) func(cookie, stringForIndent(indent-1));
313        func(cookie, "};");
314    }
315}
316
317}; // namespace android
318
319