File size: 2,424 Bytes
d601f7c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// Buffer di stringa crescente con escaping JSON.
#pragma once
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct { char *p; size_t n, cap; } SBuf;

static inline void sb_reserve(SBuf *s, size_t extra) {
    if (s->n + extra + 1 <= s->cap) return;
    size_t cap = s->cap ? s->cap : 256;
    while (cap < s->n + extra + 1) cap *= 2;
    s->p = realloc(s->p, cap);
    s->cap = cap;
}

static inline void sb_putn(SBuf *s, const char *str, size_t n) {
    sb_reserve(s, n);
    memcpy(s->p + s->n, str, n);
    s->n += n;
    s->p[s->n] = 0;
}

static inline void sb_puts(SBuf *s, const char *str) { sb_putn(s, str, strlen(str)); }

static inline void sb_printf(SBuf *s, const char *fmt, ...) {
    va_list ap, ap2;
    va_start(ap, fmt);
    va_copy(ap2, ap);
    int n = vsnprintf(NULL, 0, fmt, ap);
    va_end(ap);
    if (n > 0) {
        sb_reserve(s, (size_t)n);
        vsnprintf(s->p + s->n, (size_t)n + 1, fmt, ap2);
        s->n += (size_t)n;
    }
    va_end(ap2);
}

// Lunghezza di una sequenza UTF-8 valida che inizia in c (0 se non valida o incompleta).
static inline int utf8_len(const unsigned char *c) {
    int n = c[0] < 0x80 ? 1 : (c[0] >> 5) == 6 ? 2 : (c[0] >> 4) == 14 ? 3 : (c[0] >> 3) == 30 ? 4 : 0;
    for (int i = 1; i < n; i++) if ((c[i] & 0xC0) != 0x80) return 0;
    return n;
}

// Stringa JSON tra virgolette, con escaping. I byte UTF-8 non validi diventano U+FFFD:
// un solo byte sbagliato farebbe chiudere la connessione al browser.
static inline void sb_json_str(SBuf *s, const char *str) {
    sb_putn(s, "\"", 1);
    for (const unsigned char *c = (const unsigned char *)str; *c; c++) {
        if (*c >= 0x80) {
            int n = utf8_len(c);
            if (n) { sb_putn(s, (const char *)c, (size_t)n); c += n - 1; }
            else sb_putn(s, "\\ufffd", 6);
            continue;
        }
        switch (*c) {
            case '"':  sb_putn(s, "\\\"", 2); break;
            case '\\': sb_putn(s, "\\\\", 2); break;
            case '\n': sb_putn(s, "\\n", 2); break;
            case '\r': sb_putn(s, "\\r", 2); break;
            case '\t': sb_putn(s, "\\t", 2); break;
            default:
                if (*c < 0x20) sb_printf(s, "\\u%04x", *c);
                else sb_putn(s, (const char *)c, 1);
        }
    }
    sb_putn(s, "\"", 1);
}

static inline void sb_free(SBuf *s) { free(s->p); s->p = NULL; s->n = s->cap = 0; }