packages feed

json-stream-0.3.2.0: c_lib/unescape_string.c

/**
  * Copied from https://github.com/agrafix/highjson project.
  */

#include <stdlib.h>

void bs_json_unescape(const unsigned long length, int *error, char *bsIn, char *bsOut);

inline void private_parse_hex(const char a, const char b, const char c, const char d, int *number, short *error) {
    const char hex[5] = { a, b, c, d, '\0' };
    char *internalError;
    *number = (int)strtol(hex, &internalError, 16);
    if (*internalError != '\0') {
        *error = 1;
    } else {
        *error = 0;
    }
}

#define UTF8_CHAR(N, OUT) \
    if (N < 0x80) { \
        OUT = (char)N; \
    } else if (N < 0x800) { \
        OUT = (char)((N >> 6) | 0xc0); \
        OUT = (char)((N & 0x3F) | 0x80); \
    } else if (N < 0xffff) { \
        OUT = (char)((N >> 12) | 0xe0); \
        OUT = (char)(((N >> 6) & 0x3f) | 0x80); \
        OUT = (char)((N & 0x3f) | 0x80); \
    } else { \
        OUT = (char)((N >> 18) | 0xF0); \
        OUT = (char)(((N >> 12) & 0x3F) | 0x80); \
        OUT = (char)(((N >> 6) & 0x3F) | 0x80); \
        OUT = (char)((N & 0x3f) | 0x80); \
    }

void bs_json_unescape(const unsigned long length, int *error, char *bsIn, char *bsOut)
{
    register unsigned long ptr = 0;
    while (ptr < length) {
        const char ch = *bsIn++;
        ptr++;
        if (ch == '\\') {
            if (ptr >= length) {
                *error = 1;
                return;
            }
            const char nextCh = *bsIn++;
            ptr++;
            switch (nextCh) {
            case '\\':
                *bsOut++ = '\\';
                break;
            case '"':
                *bsOut++ = '"';
                break;
            case '/':
                *bsOut++ = '/';
                break;
            case 'b':
                *bsOut++ = '\b';
                break;
            case 'f':
                *bsOut++ = '\f';
                break;
            case 'n':
                *bsOut++ = '\n';
                break;
            case 'r':
                *bsOut++ = '\r';
                break;
            case 't':
                *bsOut++ = '\t';
                break;
            case 'u':
                if (ptr+3 >= length) {
                    *error = 3;
                    return;
                }
                ptr += 4;
                int number = 0;
                short hexError = 0;
                const char a = *bsIn++; const char b = *bsIn++; const char c = *bsIn++; const char d = *bsIn++;
                private_parse_hex(a, b, c, d, &number, &hexError);
                if (hexError == 1) {
                    *error = 4;
                    return;
                }
                if (number < 0xd800 || number > 0xdfff) {
                    UTF8_CHAR(number, *bsOut++);
                } else if (number <= 0xdbff) {
                    if (ptr+5 >= length) {
                        *error = 5;
                        return;
                    }
                    ptr += 6;
                    if (*bsIn++ != '\\') {
                        *error = 6;
                        return;
                    }
                    if (*bsIn++ != 'u') {
                        *error = 7;
                        return;
                    }
                    int numberB = 0;
                    short hexErrorB = 0;
                    const char ai = *bsIn++; const char bi = *bsIn++; const char ci = *bsIn++; const char di = *bsIn++;
                    private_parse_hex(ai, bi, ci, di, &numberB, &hexErrorB);
                    if (hexErrorB == 1) {
                        *error = 8;
                        return;
                    }
                    if (numberB >= 0xdc00 && numberB <= 0xdfff) {
                        const int r = ((number - 0xdc00) << 10) + (numberB - 0xdc00) + 0x10000;
                        UTF8_CHAR(r, *bsOut++);
                    } else {
                        *error = 9;
                        return;
                    }
                }
                break;
            default:
                *error = 2;
                return;
            }
        } else {
            *bsOut++ = ch;
        }
    }
    *bsOut = '\0';
    *error = 0;
}