| /* |
| * Copyright (C) 2005 The Android Open Source Project |
| * |
| * 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. |
| */ |
| |
| #include <utils/Debug.h> |
| |
| #include <utils/misc.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| |
| namespace android { |
| |
| // --------------------------------------------------------------------- |
| |
| static const char indentStr[] = |
| " " |
| " "; |
| |
| const char* stringForIndent(int32_t indentLevel) |
| { |
| ssize_t off = sizeof(indentStr)-1-(indentLevel*2); |
| return indentStr + (off < 0 ? 0 : off); |
| } |
| |
| // --------------------------------------------------------------------- |
| |
| static void defaultPrintFunc(void* cookie, const char* txt) |
| { |
| printf("%s", txt); |
| } |
| |
| // --------------------------------------------------------------------- |
| |
| static inline int isident(int c) |
| { |
| return isalnum(c) || c == '_'; |
| } |
| |
| static inline bool isasciitype(char c) |
| { |
| if( c >= ' ' && c < 127 && c != '\'' && c != '\\' ) return true; |
| return false; |
| } |
| |
| static inline char makehexdigit(uint32_t val) |
| { |
| return "0123456789abcdef"[val&0xF]; |
| } |
| |
| static char* appendhexnum(uint32_t val, char* out) |
| { |
| for( int32_t i=28; i>=0; i-=4 ) { |
| *out++ = makehexdigit( val>>i ); |
| } |
| *out = 0; |
| return out; |
| } |
| |
| static inline char makeupperhexdigit(uint32_t val) |
| { |
| return "0123456789ABCDEF"[val&0xF]; |
| } |
| |
| static char* appendupperhexnum(uint32_t val, char* out) |
| { |
| for( int32_t i=28; i>=0; i-=4 ) { |
| *out++ = makeupperhexdigit( val>>i ); |
| } |
| *out = 0; |
| return out; |
| } |
| |
| static char* appendcharornum(char c, char* out, bool skipzero = true) |
| { |
| if (skipzero && c == 0) return out; |
| |
| if (isasciitype(c)) { |
| *out++ = c; |
| return out; |
| } |
| |
| *out++ = '\\'; |
| *out++ = 'x'; |
| *out++ = makehexdigit(c>>4); |
| *out++ = makehexdigit(c); |
| return out; |
| } |
| |
| static char* typetostring(uint32_t type, char* out, |
| bool fullContext = true, |
| bool strict = false) |
| { |
| char* pos = out; |
| char c[4]; |
| c[0] = (char)((type>>24)&0xFF); |
| c[1] = (char)((type>>16)&0xFF); |
| c[2] = (char)((type>>8)&0xFF); |
| c[3] = (char)(type&0xFF); |
| bool valid; |
| if( !strict ) { |
| // now even less strict! |
| // valid = isasciitype(c[3]); |
| valid = true; |
| int32_t i = 0; |
| bool zero = true; |
| while (valid && i<3) { |
| if (c[i] == 0) { |
| if (!zero) valid = false; |
| } else { |
| zero = false; |
| //if (!isasciitype(c[i])) valid = false; |
| } |
| i++; |
| } |
| // if all zeros, not a valid type code. |
| if (zero) valid = false; |
| } else { |
| valid = isident(c[3]) ? true : false; |
| int32_t i = 0; |
| bool zero = true; |
| while (valid && i<3) { |
| if (c[i] == 0) { |
| if (!zero) valid = false; |
| } else { |
| zero = false; |
| if (!isident(c[i])) valid = false; |
| } |
| i++; |
| } |
| } |
| if( valid && (!fullContext || c[0] != '0' || c[1] != 'x') ) { |
| if( fullContext ) *pos++ = '\''; |
| pos = appendcharornum(c[0], pos); |
| pos = appendcharornum(c[1], pos); |
| pos = appendcharornum(c[2], pos); |
| pos = appendcharornum(c[3], pos); |
| if( fullContext ) *pos++ = '\''; |
| *pos = 0; |
| return pos; |
| } |
| |
| if( fullContext ) { |
| *pos++ = '0'; |
| *pos++ = 'x'; |
| } |
| return appendhexnum(type, pos); |
| } |
| |
| void printTypeCode(uint32_t typeCode, debugPrintFunc func, void* cookie) |
| { |
| char buffer[32]; |
| char* end = typetostring(typeCode, buffer); |
| *end = 0; |
| func ? (*func)(cookie, buffer) : defaultPrintFunc(cookie, buffer); |
| } |
| |
| void printHexData(int32_t indent, const void *buf, size_t length, |
| size_t bytesPerLine, int32_t singleLineBytesCutoff, |
| size_t alignment, bool cStyle, |
| debugPrintFunc func, void* cookie) |
| { |
| if (alignment == 0) { |
| if (bytesPerLine >= 16) alignment = 4; |
| else if (bytesPerLine >= 8) alignment = 2; |
| else alignment = 1; |
| } |
| if (func == NULL) func = defaultPrintFunc; |
| |
| size_t offset; |
| |
| unsigned char *pos = (unsigned char *)buf; |
| |
| if (pos == NULL) { |
| if (singleLineBytesCutoff < 0) func(cookie, "\n"); |
| func(cookie, "(NULL)"); |
| return; |
| } |
| |
| if (length == 0) { |
| if (singleLineBytesCutoff < 0) func(cookie, "\n"); |
| func(cookie, "(empty)"); |
| return; |
| } |
| |
| if ((int32_t)length < 0) { |
| if (singleLineBytesCutoff < 0) func(cookie, "\n"); |
| char buf[64]; |
| sprintf(buf, "(bad length: %zu)", length); |
| func(cookie, buf); |
| return; |
| } |
| |
| char buffer[256]; |
| static const size_t maxBytesPerLine = (sizeof(buffer)-1-11-4)/(3+1); |
| |
| if (bytesPerLine > maxBytesPerLine) bytesPerLine = maxBytesPerLine; |
| |
| const bool oneLine = (int32_t)length <= singleLineBytesCutoff; |
| bool newLine = false; |
| if (cStyle) { |
| indent++; |
| func(cookie, "{\n"); |
| newLine = true; |
| } else if (!oneLine) { |
| func(cookie, "\n"); |
| newLine = true; |
| } |
| |
| for (offset = 0; ; offset += bytesPerLine, pos += bytesPerLine) { |
| long remain = length; |
| |
| char* c = buffer; |
| if (!oneLine && !cStyle) { |
| sprintf(c, "0x%08x: ", (int)offset); |
| c += 12; |
| } |
| |
| size_t index; |
| size_t word; |
| |
| for (word = 0; word < bytesPerLine; ) { |
| |
| #ifdef HAVE_LITTLE_ENDIAN |
| const size_t startIndex = word+(alignment-(alignment?1:0)); |
| const ssize_t dir = -1; |
| #else |
| const size_t startIndex = word; |
| const ssize_t dir = 1; |
| #endif |
| |
| for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) { |
| |
| if (!cStyle) { |
| if (index == 0 && word > 0 && alignment > 0) { |
| *c++ = ' '; |
| } |
| |
| if (remain-- > 0) { |
| const unsigned char val = *(pos+startIndex+(index*dir)); |
| *c++ = makehexdigit(val>>4); |
| *c++ = makehexdigit(val); |
| } else if (!oneLine) { |
| *c++ = ' '; |
| *c++ = ' '; |
| } |
| } else { |
| if (remain > 0) { |
| if (index == 0 && word > 0) { |
| *c++ = ','; |
| *c++ = ' '; |
| } |
| if (index == 0) { |
| *c++ = '0'; |
| *c++ = 'x'; |
| } |
| const unsigned char val = *(pos+startIndex+(index*dir)); |
| *c++ = makehexdigit(val>>4); |
| *c++ = makehexdigit(val); |
| remain--; |
| } |
| } |
| } |
| |
| word += index; |
| } |
| |
| if (!cStyle) { |
| remain = length; |
| *c++ = ' '; |
| *c++ = '\''; |
| for (index = 0; index < bytesPerLine; index++) { |
| |
| if (remain-- > 0) { |
| const unsigned char val = pos[index]; |
| *c++ = (val >= ' ' && val < 127) ? val : '.'; |
| } else if (!oneLine) { |
| *c++ = ' '; |
| } |
| } |
| |
| *c++ = '\''; |
| if (length > bytesPerLine) *c++ = '\n'; |
| } else { |
| if (remain > 0) *c++ = ','; |
| *c++ = '\n'; |
| } |
| |
| if (newLine && indent) func(cookie, stringForIndent(indent)); |
| *c = 0; |
| func(cookie, buffer); |
| newLine = true; |
| |
| if (length <= bytesPerLine) break; |
| length -= bytesPerLine; |
| } |
| |
| if (cStyle) { |
| if (indent > 0) func(cookie, stringForIndent(indent-1)); |
| func(cookie, "};"); |
| } |
| } |
| |
| }; // namespace android |
| |