uint32_t
ngx_utf8_decode(u_char **p, size_t n)
{
    size_t    len;
    uint32_t  u, i, valid;
    u = **p;
    if (u > 0xf0) {
        u &= 0x07;
        valid = 0xffff;
        len = 3;
    } else if (u > 0xe0) {
        u &= 0x0f;
        valid = 0x7ff;
        len = 2;
    } else if (u > 0xc0) {
        u &= 0x1f;
        valid = 0x7f;
        len = 1;
    } else {
        (*p)++;
        return 0xffffffff;
    }
    if (n - 1 < len) {
        return 0xfffffffe;
    }
    (*p)++;
    while (len) {
        i = *(*p)++;
        if (i < 0x80) {
            return 0xffffffff;
        }
        u = (u << 6) | (i & 0x3f);
        len--;
    }
    if (u > valid) {
        return u;
    }
    return 0xffffffff;
}
['static ngx_chain_t *\nngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf,\n    ngx_http_charset_ctx_t *ctx)\n{\n    size_t        len, size;\n    u_char        c, *p, *src, *dst, *saved, **table;\n    uint32_t      n;\n    ngx_buf_t    *b;\n    ngx_uint_t    i;\n    ngx_chain_t  *out, *cl, **ll;\n    src = buf->pos;\n    if (ctx->saved_len == 0) {\n        for (   ; src < buf->last; src++) {\n            if (*src < 0x80) {\n                continue;\n            }\n            len = src - buf->pos;\n            if (len > 512) {\n                out = ngx_http_charset_get_buf(pool, ctx);\n                if (out == NULL) {\n                    return NULL;\n                }\n                b = out->buf;\n                b->temporary = buf->temporary;\n                b->memory = buf->memory;\n                b->mmap = buf->mmap;\n                b->flush = buf->flush;\n                b->pos = buf->pos;\n                b->last = src;\n                out->buf = b;\n                out->next = NULL;\n                size = buf->last - src;\n                saved = src;\n                n = ngx_utf8_decode(&saved, size);\n                if (n == 0xfffffffe) {\n                    ngx_memcpy(ctx->saved, src, size);\n                    ctx->saved_len = size;\n                    b->shadow = buf;\n                    return out;\n                }\n            } else {\n                out = NULL;\n                size = len + buf->last - src;\n                src = buf->pos;\n            }\n            if (size < NGX_HTML_ENTITY_LEN) {\n                size += NGX_HTML_ENTITY_LEN;\n            }\n            cl = ngx_http_charset_get_buffer(pool, ctx, size);\n            if (cl == NULL) {\n                return NULL;\n            }\n            if (out) {\n                out->next = cl;\n            } else {\n                out = cl;\n            }\n            b = cl->buf;\n            dst = b->pos;\n            goto recode;\n        }\n        out = ngx_alloc_chain_link(pool);\n        if (out == NULL) {\n            return NULL;\n        }\n        out->buf = buf;\n        out->next = NULL;\n        return out;\n    }\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                   "http charset utf saved: %z", ctx->saved_len);\n    p = src;\n    for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) {\n        ctx->saved[i] = *p++;\n        if (p == buf->last) {\n            break;\n        }\n    }\n    saved = ctx->saved;\n    n = ngx_utf8_decode(&saved, i);\n    c = \'\\0\';\n    if (n < 0x10000) {\n        table = (u_char **) ctx->table;\n        p = table[n >> 8];\n        if (p) {\n            c = p[n & 0xff];\n        }\n    } else if (n == 0xfffffffe) {\n        if (i < NGX_UTF_LEN) {\n            out = ngx_http_charset_get_buf(pool, ctx);\n            if (out == NULL) {\n                return NULL;\n            }\n            b = out->buf;\n            b->pos = buf->pos;\n            b->last = buf->last;\n            b->sync = 1;\n            b->shadow = buf;\n            ngx_memcpy(&ctx->saved[ctx->saved_len], src, i);\n            ctx->saved_len += i;\n            return out;\n        }\n    }\n    size = buf->last - buf->pos;\n    if (size < NGX_HTML_ENTITY_LEN) {\n        size += NGX_HTML_ENTITY_LEN;\n    }\n    cl = ngx_http_charset_get_buffer(pool, ctx, size);\n    if (cl == NULL) {\n        return NULL;\n    }\n    out = cl;\n    b = cl->buf;\n    dst = b->pos;\n    if (c) {\n        *dst++ = c;\n    } else if (n == 0xfffffffe) {\n        *dst++ = \'?\';\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                       "http charset invalid utf 0");\n        saved = &ctx->saved[NGX_UTF_LEN];\n    } else if (n > 0x10ffff) {\n        *dst++ = \'?\';\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                       "http charset invalid utf 1");\n    } else {\n        dst = ngx_sprintf(dst, "&#%uD;", n);\n    }\n    src += (saved - ctx->saved) - ctx->saved_len;\n    ctx->saved_len = 0;\nrecode:\n    ll = &cl->next;\n    table = (u_char **) ctx->table;\n    while (src < buf->last) {\n        if ((size_t) (b->end - dst) < NGX_HTML_ENTITY_LEN) {\n            b->last = dst;\n            size = buf->last - src + NGX_HTML_ENTITY_LEN;\n            cl = ngx_http_charset_get_buffer(pool, ctx, size);\n            if (cl == NULL) {\n                return NULL;\n            }\n            *ll = cl;\n            ll = &cl->next;\n            b = cl->buf;\n            dst = b->pos;\n        }\n        if (*src < 0x80) {\n            *dst++ = *src++;\n            continue;\n        }\n        len = buf->last - src;\n        n = ngx_utf8_decode(&src, len);\n        if (n < 0x10000) {\n            p = table[n >> 8];\n            if (p) {\n                c = p[n & 0xff];\n                if (c) {\n                    *dst++ = c;\n                    continue;\n                }\n            }\n            dst = ngx_sprintf(dst, "&#%uD;", n);\n            continue;\n        }\n        if (n == 0xfffffffe) {\n            ngx_memcpy(ctx->saved, src, len);\n            ctx->saved_len = len;\n            if (b->pos == dst) {\n                b->sync = 1;\n                b->temporary = 0;\n            }\n            break;\n        }\n        if (n > 0x10ffff) {\n            *dst++ = \'?\';\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                           "http charset invalid utf 2");\n            continue;\n        }\n        dst = ngx_sprintf(dst, "&#%uD;", n);\n    }\n    b->last = dst;\n    b->last_buf = buf->last_buf;\n    b->last_in_chain = buf->last_in_chain;\n    b->flush = buf->flush;\n    b->shadow = buf;\n    return out;\n}', 'uint32_t\nngx_utf8_decode(u_char **p, size_t n)\n{\n    size_t    len;\n    uint32_t  u, i, valid;\n    u = **p;\n    if (u > 0xf0) {\n        u &= 0x07;\n        valid = 0xffff;\n        len = 3;\n    } else if (u > 0xe0) {\n        u &= 0x0f;\n        valid = 0x7ff;\n        len = 2;\n    } else if (u > 0xc0) {\n        u &= 0x1f;\n        valid = 0x7f;\n        len = 1;\n    } else {\n        (*p)++;\n        return 0xffffffff;\n    }\n    if (n - 1 < len) {\n        return 0xfffffffe;\n    }\n    (*p)++;\n    while (len) {\n        i = *(*p)++;\n        if (i < 0x80) {\n            return 0xffffffff;\n        }\n        u = (u << 6) | (i & 0x3f);\n        len--;\n    }\n    if (u > valid) {\n        return u;\n    }\n    return 0xffffffff;\n}']