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 <binder/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 char* appendcharornum(char c, char* out, bool skipzero = true)
74{
75    if (skipzero && c == 0) return out;
76
77    if (isasciitype(c)) {
78        *out++ = c;
79        return out;
80    }
81
82    *out++ = '\\';
83    *out++ = 'x';
84    *out++ = makehexdigit(c>>4);
85    *out++ = makehexdigit(c);
86    return out;
87}
88
89static char* typetostring(uint32_t type, char* out,
90                          bool fullContext = true,
91                          bool strict = false)
92{
93    char* pos = out;
94    char c[4];
95    c[0] = (char)((type>>24)&0xFF);
96    c[1] = (char)((type>>16)&0xFF);
97    c[2] = (char)((type>>8)&0xFF);
98    c[3] = (char)(type&0xFF);
99    bool valid;
100    if( !strict ) {
101        // now even less strict!
102        // valid = isasciitype(c[3]);
103        valid = true;
104        int32_t i = 0;
105        bool zero = true;
106        while (valid && i<3) {
107            if (c[i] == 0) {
108                if (!zero) valid = false;
109            } else {
110                zero = false;
111                //if (!isasciitype(c[i])) valid = false;
112            }
113            i++;
114        }
115        // if all zeros, not a valid type code.
116        if (zero) valid = false;
117    } else {
118        valid = isident(c[3]) ? true : false;
119        int32_t i = 0;
120        bool zero = true;
121        while (valid && i<3) {
122            if (c[i] == 0) {
123                if (!zero) valid = false;
124            } else {
125                zero = false;
126                if (!isident(c[i])) valid = false;
127            }
128            i++;
129        }
130    }
131    if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) {
132        if( fullContext ) *pos++ = '\'';
133        pos = appendcharornum(c[0], pos);
134        pos = appendcharornum(c[1], pos);
135        pos = appendcharornum(c[2], pos);
136        pos = appendcharornum(c[3], pos);
137        if( fullContext ) *pos++ = '\'';
138        *pos = 0;
139        return pos;
140    }
141
142    if( fullContext ) {
143        *pos++ = '0';
144        *pos++ = 'x';
145    }
146    return appendhexnum(type, pos);
147}
148
149void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie)
150{
151    char buffer[32];
152    char* end = typetostring(typeCode, buffer);
153    *end = 0;
154    func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer);
155}
156
157void printHexData(int32_t indent, const void *buf, size_t length,
158    size_t bytesPerLine, int32_t singleLineBytesCutoff,
159    size_t alignment, bool cStyle,
160    debugPrintFunc func, void* cookie)
161{
162    if (alignment == 0) {
163        if (bytesPerLine >= 16) alignment = 4;
164        else if (bytesPerLine >= 8) alignment = 2;
165        else alignment = 1;
166    }
167    if (func == NULL) func = defaultPrintFunc;
168
169    size_t offset;
170
171    unsigned char *pos = (unsigned char *)buf;
172
173    if (pos == NULL) {
174        if (singleLineBytesCutoff < 0) func(cookie, "\n");
175        func(cookie, "(NULL)");
176        return;
177    }
178
179    if (length == 0) {
180        if (singleLineBytesCutoff < 0) func(cookie, "\n");
181        func(cookie, "(empty)");
182        return;
183    }
184
185    if ((int32_t)length < 0) {
186        if (singleLineBytesCutoff < 0) func(cookie, "\n");
187        char buf[64];
188        sprintf(buf, "(bad length: %zu)", length);
189        func(cookie, buf);
190        return;
191    }
192
193    char buffer[256];
194    static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1);
195
196    if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine;
197
198    const bool oneLine = (int32_t)length <= singleLineBytesCutoff;
199    bool newLine = false;
200    if (cStyle) {
201        indent++;
202        func(cookie, "{\n");
203        newLine = true;
204    } else if (!oneLine) {
205        func(cookie, "\n");
206        newLine = true;
207    }
208
209    for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) {
210        long remain = length;
211
212        char* c = buffer;
213        if (!oneLine && !cStyle) {
214            sprintf(c, "0x%08x: ", (int)offset);
215            c += 12;
216        }
217
218        size_t index;
219        size_t word;
220
221        for (word = 0; word < bytesPerLine; ) {
222
223#ifdef HAVE_LITTLE_ENDIAN
224            const size_t startIndex = word+(alignment-(alignment?1:0));
225            const ssize_t dir = -1;
226#else
227            const size_t startIndex = word;
228            const ssize_t dir = 1;
229#endif
230
231            for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) {
232
233                if (!cStyle) {
234                    if (index == 0 && word > 0 && alignment > 0) {
235                        *c++ = ' ';
236                    }
237
238                    if (remain-- > 0) {
239                        const unsigned char val = *(pos+startIndex+(index*dir));
240                        *c++ = makehexdigit(val>>4);
241                        *c++ = makehexdigit(val);
242                    } else if (!oneLine) {
243                        *c++ = ' ';
244                        *c++ = ' ';
245                    }
246                } else {
247                    if (remain > 0) {
248                        if (index == 0 && word > 0) {
249                            *c++ = ',';
250                            *c++ = ' ';
251                        }
252                        if (index == 0) {
253                            *c++ = '0';
254                            *c++ = 'x';
255                        }
256                        const unsigned char val = *(pos+startIndex+(index*dir));
257                        *c++ = makehexdigit(val>>4);
258                        *c++ = makehexdigit(val);
259                        remain--;
260                    }
261                }
262            }
263
264            word += index;
265        }
266
267        if (!cStyle) {
268            remain = length;
269            *c++ = ' ';
270            *c++ = '\'';
271            for (index = 0; index < bytesPerLine; index++) {
272
273                if (remain-- > 0) {
274                    const unsigned char val = pos[index];
275                    *c++ = (val >= ' ' && val < 127) ? val : '.';
276                } else if (!oneLine) {
277                    *c++ = ' ';
278                }
279            }
280
281            *c++ = '\'';
282            if (length > bytesPerLine) *c++ = '\n';
283        } else {
284            if (remain > 0) *c++ = ',';
285            *c++ = '\n';
286        }
287
288        if (newLine && indent) func(cookie, stringForIndent(indent));
289        *c = 0;
290        func(cookie, buffer);
291        newLine = true;
292
293        if (length <= bytesPerLine) break;
294        length -= bytesPerLine;
295    }
296
297    if (cStyle) {
298        if (indent > 0) func(cookie, stringForIndent(indent-1));
299        func(cookie, "};");
300    }
301}
302
303}; // namespace android
304
305