/*
* 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