Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,202 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2022 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - changes to SOURCES
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
if (NOT LWS_ONLY_SSPC)
list(APPEND SOURCES
misc/base64-decode.c
misc/prng.c
misc/lws-ring.c)
if (LWS_WITH_NETWORK)
list(APPEND SOURCES
misc/cache-ttl/lws-cache-ttl.c
misc/cache-ttl/heap.c
)
if (LWS_WITH_CACHE_NSCOOKIEJAR)
list(APPEND SOURCES
misc/cache-ttl/file.c)
endif()
endif()
if (LWS_WITH_COMPRESSED_BACKTRACES)
list(APPEND SOURCES
misc/backtrace.c)
endif()
if (LWS_WITH_FTS)
list(APPEND SOURCES
misc/fts/trie.c
misc/fts/trie-fd.c)
endif()
if (LWS_WITH_GZINFLATE)
list(APPEND SOURCES
misc/upng-gzip.c)
endif()
if (LWS_WITH_UPNG)
list(APPEND SOURCES
misc/upng.c)
endif()
if (LWS_WITH_JPEG)
list(APPEND SOURCES
misc/jpeg.c)
endif()
if (LWS_WITH_DLO)
list(APPEND SOURCES
misc/dlo/dlo.c
misc/dlo/dlo-rect.c
misc/dlo/dlo-font-mcufont.c
misc/dlo/dlo-text.c)
if (LWS_WITH_SECURE_STREAMS AND LWS_WITH_CLIENT)
list(APPEND SOURCES
misc/dlo/dlo-ss.c)
endif()
if (LWS_WITH_UPNG)
list(APPEND SOURCES
misc/dlo/dlo-png.c)
endif()
if (LWS_WITH_JPEG)
list(APPEND SOURCES
misc/dlo/dlo-jpeg.c)
endif()
if (LWS_WITH_LHP)
list(APPEND SOURCES
misc/dlo/dlo-lhp.c
)
endif()
endif() #dlo
# this is an older, standalone hashed disk cache
# implementation unrelated to lws-cache-ttl
if (LWS_WITH_DISKCACHE)
list(APPEND SOURCES
misc/diskcache.c)
endif()
if (LWS_WITH_STRUCT_JSON)
list(APPEND SOURCES
misc/lws-struct-lejp.c)
endif()
if (LWS_WITH_JSONRPC)
list(APPEND SOURCES
misc/jrpc/jrpc.c)
include_directories(misc/jrpc)
endif()
if (LWS_WITH_STRUCT_SQLITE3)
list(APPEND SOURCES
misc/lws-struct-sqlite.c)
endif()
if (LWS_WITH_FSMOUNT AND ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
list(APPEND SOURCES misc/fsmount.c)
endif()
if (LWS_WITH_DIR AND NOT LWS_PLAT_BAREMETAL)
list(APPEND SOURCES misc/dir.c)
endif()
if (LWS_WITH_THREADPOOL AND LWS_HAVE_PTHREAD_H)
list(APPEND SOURCES misc/threadpool/threadpool.c)
endif()
if (LWS_WITH_PEER_LIMITS)
list(APPEND SOURCES
misc/peer-limits.c)
endif()
if (LWS_WITH_LWSAC)
list(APPEND SOURCES
misc/lwsac/lwsac.c)
if (NOT LWS_PLAT_FREERTOS AND NOT LWS_PLAT_BAREMETAL)
list(APPEND SOURCES
misc/lwsac/cached-file.c)
endif()
if (LWS_WITH_SECURE_STREAMS_CPP)
list(APPEND SOURCES misc/lwsac/lwsac.cxx)
endif()
endif()
if (NOT LWS_WITHOUT_BUILTIN_SHA1)
list(APPEND SOURCES
misc/sha-1.c)
endif()
if (LWS_WITH_LEJP)
list(APPEND SOURCES
misc/lejp.c)
endif()
if (LWS_WITH_CBOR)
list(APPEND SOURCES
misc/lecp.c
misc/ieeehalfprecision.c)
endif()
if (LWS_WITH_LHP)
list(APPEND SOURCES
misc/lhp.c)
if (LWS_WITH_SECURE_STREAMS)
list(APPEND SOURCES
misc/lhp-ss.c)
endif()
endif()
if (UNIX)
if (NOT LWS_HAVE_GETIFADDRS)
list(APPEND HDR_PRIVATE misc/getifaddrs.h)
list(APPEND SOURCES misc/getifaddrs.c)
endif()
endif()
if (NOT WIN32 AND NOT LWS_WITHOUT_DAEMONIZE)
list(APPEND SOURCES
misc/daemonize.c)
endif()
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,392 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#define _GNU_SOURCE
#include <unwind.h>
static _Unwind_Reason_Code
uwcb(struct _Unwind_Context* uctx, void *arg)
{
lws_backtrace_info_t *si = (lws_backtrace_info_t *)arg;
if (si->sp == LWS_ARRAY_SIZE(si->st))
return _URC_END_OF_STACK;
if (!si->pre) {
if (_Unwind_GetIP(uctx))
si->st[si->sp++] = _Unwind_GetIP(uctx);
} else
si->pre--;
return _URC_NO_REASON;
}
int
lws_backtrace(lws_backtrace_info_t *si, uint8_t pre, uint8_t post)
{
_Unwind_Reason_Code r;
si->sp = 0;
si->pre = pre; /* skip the top couple of backtrace results */
si->post = post;
r = _Unwind_Backtrace(uwcb, si);
if (si->sp > si->post)
si->sp -= si->post;
return r != _URC_END_OF_STACK;
}
int
lws_backtrace_compression_stream(lws_backtrace_comp_t *c, uintptr_t v,
unsigned int bits)
{
int nbits = (int)bits;
while (nbits-- >= 0) {
if (!(c->pos & 7))
c->comp[c->pos >> 3] = 0;
if (v & (1 << nbits))
c->comp[c->pos >> 3] |= (1 << (7 - (c->pos & 7)));
c->pos++;
if ((c->pos >> 3) == c->len) {
lwsl_err("%s: overrun %u\n", __func__, (unsigned int)c->len);
return 1;
}
}
return 0;
}
int
lws_backtrace_compression_destream(lws_backtrace_comp_t *c, uintptr_t *_v,
unsigned int bits)
{
int nbits = (int)bits;
uintptr_t v = 0;
while (nbits-- >= 0) {
if ((c->pos >> 3) == c->len)
return 1;
if (c->comp[c->pos >> 3] & (1 << (7 - (c->pos & 7))))
v |= (1 << nbits);
c->pos++;
}
*_v = v;
return 0;
}
void
lws_backtrace_compression_stream_init(lws_backtrace_comp_t *c,
uint8_t *comp, size_t comp_len)
{
*comp = 0;
c->pos = 0;
c->comp = comp;
c->len = comp_len;
}
int
lws_backtrace_compress_backtrace(lws_backtrace_info_t *si,
lws_backtrace_comp_t *c)
{
int n;
lws_backtrace_compression_stream(c, si->sp, 5);
for (n = 0; n < si->sp; n++) { /* go through each in turn */
uintptr_t delta = (uintptr_t)~0ll, d1;
char hit = -1, sign, _sign;
unsigned int q, ql;
int m;
if (n > 8)
m = n - 8;
else
m = 0;
/* we can look for 1 to 8 back */
for (; m < n; m++) {
if (si->st[n] > si->st[m]) {
d1 = si->st[n] - si->st[m];
_sign = 0;
} else {
d1 = si->st[m] - si->st[n];
_sign = 1;
}
if (d1 < delta) {
delta = d1;
hit = (char)m;
sign = _sign;
}
}
q = lws_sigbits(delta);
ql = lws_sigbits(si->st[n]);
/*
* Bitwise compression:
*
* 0: zzzzzz literal (number of bits following)
* 1: xxx: y: zzzzzz delta (base index is (xxx + 1) back
* from this index)
* y == 1 == subtract from base,
* zzzzzz delta bits follow
*/
if (n && hit && q + 11 < ql + 7) {
/* shorter to issue a delta froma previous address */
lws_backtrace_compression_stream(c, 1, 1);
lws_backtrace_compression_stream(c, (uintptr_t)((n - hit) - 1), 3);
lws_backtrace_compression_stream(c, (uintptr_t)sign, 1);
lws_backtrace_compression_stream(c, q, 6);
if (lws_backtrace_compression_stream(c, delta, q))
return 1;
} else {
/* shorter to issue a literal */
lws_backtrace_compression_stream(c, 0, 1);
lws_backtrace_compression_stream(c, ql, 6);
if (lws_backtrace_compression_stream(c, si->st[n], ql))
return 1;
}
}
return 0;
}
void
lws_alloc_metadata_gen(size_t size, uint8_t *comp, size_t comp_len,
size_t *adj, size_t *cl)
{
lws_backtrace_info_t si;
lws_backtrace_comp_t c;
unsigned int q, ql;
/**< We need enough here to take the compressed results of however many
* callstack Instruction Pointers are allowed, currently 16.
*/
lws_backtrace_compression_stream_init(&c, comp, comp_len);
lws_backtrace(&si, LWS_COMPRESSED_BACKTRACES_SNIP_PRE,
LWS_COMPRESSED_BACKTRACES_SNIP_POST);
/*
* We have the result stack, let's compress it
*
* - (implicit alignment)
* - call stack len (5b) / call stack literal [ { literal | delta } ... ]
* - bitcount(6), alloc size literal
*
* - 2 bytes MSB-first at end on byte boundary, total compressed length
* behind it.
* - lws_dll2_t
*/
if (!lws_backtrace_compress_backtrace(&si, &c)) {
lws_backtrace_compression_stream(&c, lws_sigbits(size), 6);
lws_backtrace_compression_stream(&c, size, lws_sigbits(size));
q = (unsigned int)(c.pos >> 3);
if (c.pos & 7)
q++;
if (q + 2 >= c.len) {
lwsl_err("ovf\n");
goto nope;
}
ql = q + 2;
c.comp[q++] = (uint8_t)((ql >> 8) & 0xff);
c.comp[q++] = (uint8_t)(ql & 0xff);
/*
* So we have it compressed along with our additional data.
*/
/* pointer-aligned total overallocation */
*adj = sizeof(lws_dll2_t) +
((q + sizeof(void *) - 1) / sizeof(void *)) *
sizeof(void *);
/* compression buf contents amount */
*cl = q;
} else {
/* put an explicit zero-length prepend for want of anything else */
nope:
c.comp[0] = 0;
c.comp[1] = 0;
c.pos = 16; /* bits */
*cl = 2;
*adj = sizeof(lws_dll2_t) + sizeof(void *);
}
}
/* incoming *v is the true allocation */
void
_lws_alloc_metadata_adjust(lws_dll2_owner_t *active, void **v, size_t adj,
uint8_t *comp, unsigned int cl)
{
/*
* Lie about the alloc start in order to conceal our metadata behind
* what was asked for. Incoming v is the real
*
* True alloc /Comp Reported alloc
* V V
* <compressed> <16-bit MSB len to comp> lws_dll2_t
*/
*v = (void *)((uint8_t *)(*v) + adj - sizeof(lws_dll2_t));
memcpy((uint8_t *)(*v) - cl, comp, cl);
lws_dll2_clear((*v));
lws_dll2_add_tail((*v), active);
*v = (void *)((uint8_t *)(*v) + sizeof(lws_dll2_t));
}
void
_lws_alloc_metadata_trim(void **ptr, uint8_t **comp, uint16_t *complen)
{
const uint8_t *p = ((const uint8_t *)*ptr) - sizeof(lws_dll2_t);
uint16_t cofs = p[-1] | (p[-2] << 8);
size_t adj = ((sizeof(lws_dll2_t) + cofs + sizeof(void *) - 1) /
sizeof(void *)) * sizeof(void *);
//lwsl_hexdump_notice((uint8_t *)(*ptr) - adj, adj);
if (comp)
*comp = (uint8_t *)p - cofs; /* start of compressed area */
if (complen)
*complen = cofs - 2;
lws_dll2_remove((lws_dll2_t *)p);
*ptr = (void *)((uint8_t *)*ptr - adj); /* original alloc point */
}
/* past_len: after the 16-bit len, pointing at the lws_dll2_t at the end */
int
lws_alloc_metadata_parse(lws_backtrace_info_t *si, const uint8_t *past_len)
{
const uint8_t *p = (const uint8_t *)past_len;
uintptr_t n, entries, ri, sign, field;
uint16_t cofs = p[-1] | (p[-2] << 8);
lws_backtrace_comp_t c;
c.comp = (uint8_t *)p - cofs;
c.pos = 0;
c.len = cofs - 2;
si->sp = 0;
/* 5-bit bitfield contains callstack depth */
if (lws_backtrace_compression_destream(&c, &entries, 5))
return 1;
while (si->sp != entries) {
if (lws_backtrace_compression_destream(&c, &n, 1))
return 1;
if (n) { /* delta: 3-bit refidx, 1-bit delta sign, 6-bit fieldlen, field */
assert(si->sp); /* first must be literal */
if (lws_backtrace_compression_destream(&c, &ri, 3))
return 1;
if (lws_backtrace_compression_destream(&c, &sign, 1))
return 1;
if (lws_backtrace_compression_destream(&c, &n, 6))
return 1;
if (lws_backtrace_compression_destream(&c, &field, (unsigned int)n))
return 1;
if (si->sp < si->sp - ri - 1 ) {
lwsl_err("ref err\n");
return 1;
}
if (sign) /* backwards from ref */
si->st[si->sp] = si->st[si->sp - (ri + 1)] - field;
else /* forwards from ref */
si->st[si->sp] = si->st[si->sp - (ri + 1)] + field;
} else { /* literal */
if (lws_backtrace_compression_destream(&c, &n, 6))
return 1;
if (lws_backtrace_compression_destream(&c, &field, (unsigned int)n))
return 1;
si->st[si->sp] = field;
}
si->sp++;
}
/* 6-bit bitlength, then allocated size */
if (lws_backtrace_compression_destream(&c, &n, 6))
return 1;
if (lws_backtrace_compression_destream(&c, &si->asize, (unsigned int)n))
return 1;
return 0;
}
int
lws_alloc_metadata_dump_stdout(struct lws_dll2 *d, void *user)
{
char ab[192];
const uint8_t *p = (const uint8_t *)d;
uint16_t cofs = p[-1] | (p[-2] << 8);
p = (uint8_t *)p - cofs;
ab[0] = '~';
ab[1] = 'm';
ab[2] = '#';
lws_b64_encode_string((const char *)p, (int)cofs,
ab + 3, (int)sizeof(ab) - 4);
puts(ab);
return 0;
}
void
_lws_alloc_metadata_dump(lws_dll2_owner_t *active, lws_dll2_foreach_cb_t cb,
void *arg)
{
lws_dll2_foreach_safe(active, arg, cb);
}

View File

@ -0,0 +1,306 @@
/*
* This code originally came from here
*
* http://base64.sourceforge.net/b64.c
*
* already with MIT license, which is retained.
*
* LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the
* Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
* KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* VERSION HISTORY:
* Bob Trower 08/04/01 -- Create Version 0.00.00B
*
* I cleaned it up quite a bit to match the (linux kernel) style of the rest
* of libwebsockets
*/
#include "private-lib-core.h"
#include <stdio.h>
#include <string.h>
static const char encode_orig[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
static const char encode_url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789-_";
static const char decode[] = "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW"
"$$$$$$XYZ[\\]^_`abcdefghijklmnopq";
static int
_lws_b64_encode_string(const char *encode, const char *in, int in_len,
char *out, int out_size)
{
unsigned char triple[3];
int i, done = 0;
while (in_len) {
int len = 0;
for (i = 0; i < 3; i++) {
if (in_len) {
triple[i] = (unsigned char)*in++;
len++;
in_len--;
} else
triple[i] = 0;
}
if (done + 4 >= out_size)
return -1;
*out++ = encode[triple[0] >> 2];
*out++ = encode[(((triple[0] & 0x03) << 4) & 0x30) |
(((triple[1] & 0xf0) >> 4) & 0x0f)];
*out++ = (char)(len > 1 ? encode[(((triple[1] & 0x0f) << 2) & 0x3c) |
(((triple[2] & 0xc0) >> 6) & 3)] : '=');
*out++ = (char)(len > 2 ? encode[triple[2] & 0x3f] : '=');
done += 4;
}
if (done + 1 >= out_size)
return -1;
*out++ = '\0';
return done;
}
int
lws_b64_encode_string(const char *in, int in_len, char *out, int out_size)
{
return _lws_b64_encode_string(encode_orig, in, in_len, out, out_size);
}
int
lws_b64_encode_string_url(const char *in, int in_len, char *out, int out_size)
{
return _lws_b64_encode_string(encode_url, in, in_len, out, out_size);
}
void
lws_b64_decode_state_init(struct lws_b64state *state)
{
memset(state, 0, sizeof(*state));
}
int
lws_b64_decode_stateful(struct lws_b64state *s, const char *in, size_t *in_len,
uint8_t *out, size_t *out_size, int final)
{
const char *orig_in = in, *end_in = in + *in_len;
uint8_t *orig_out = out, *end_out = out + *out_size;
int equals = 0;
while (in < end_in && *in && out + 3 <= end_out) {
for (; s->i < 4 && in < end_in && *in; s->i++) {
uint8_t v;
v = 0;
s->c = 0;
while (in < end_in && *in && !v) {
s->c = v = (unsigned char)*in++;
if (v == '\x0a') {
v = 0;
continue;
}
if (v == '=') {
equals++;
v = 0;
continue;
}
/* Sanity check this is part of the charset */
if ((v < '0' || v > '9') &&
(v < 'A' || v > 'Z') &&
(v < 'a' || v > 'z') &&
v != '-' && v != '+' && v != '_' && v != '/') {
lwsl_err("%s: bad base64 0x%02X '%c' @+%d\n", __func__, v, v, lws_ptr_diff(in, orig_in));
return -1;
}
if (equals) {
lwsl_err("%s: non = after =\n", __func__);
return -1;
}
/* support the url base64 variant too */
if (v == '-')
s->c = v = '+';
if (v == '_')
s->c = v = '/';
v = (uint8_t)decode[v - 43];
if (v)
v = (uint8_t)((v == '$') ? 0 : v - 61);
}
if (s->c) {
s->len++;
if (v)
s->quad[s->i] = (uint8_t)(v - 1);
} else
s->quad[s->i] = 0;
}
if (s->i != 4 && !final)
continue;
s->i = 0;
/*
* "The '==' sequence indicates that the last group contained
* only one byte, and '=' indicates that it contained two
* bytes." (wikipedia)
*/
if (s->len >= 2 || equals > 1)
*out++ = (uint8_t)(s->quad[0] << 2 | s->quad[1] >> 4);
if (s->len >= 3 || equals)
*out++ = (uint8_t)(s->quad[1] << 4 | s->quad[2] >> 2);
if (s->len >= 4 && !equals)
*out++ = (uint8_t)(((s->quad[2] << 6) & 0xc0) | s->quad[3]);
s->done += s->len - 1;
s->len = 0;
}
if (out < end_out)
*out = '\0';
*in_len = (unsigned int)(in - orig_in);
*out_size = (unsigned int)(out - orig_out);
return 0;
}
/*
* returns length of decoded string in out, or -1 if out was too small
* according to out_size
*
* Only reads up to in_len chars, otherwise if in_len is -1 on entry reads until
* the first NUL in the input.
*/
static size_t
_lws_b64_decode_string(const char *in, int in_len, char *out, size_t out_size)
{
struct lws_b64state state;
size_t il = (size_t)in_len, ol = out_size;
if (in_len == -1)
il = strlen(in);
lws_b64_decode_state_init(&state);
if (lws_b64_decode_stateful(&state, in, &il, (uint8_t *)out, &ol, 1) < 0)
/* pass on the failure */
return 0;
if ((int)il != in_len) {
lwsl_err("%s: base64 must end at end of input\n", __func__);
return 0;
}
return ol;
}
int
lws_b64_decode_string(const char *in, char *out, int out_size)
{
return (int)_lws_b64_decode_string(in, -1, out, (unsigned int)out_size);
}
int
lws_b64_decode_string_len(const char *in, int in_len, char *out, int out_size)
{
size_t s = _lws_b64_decode_string(in, in_len, out, (unsigned int)out_size);
return !s ? -1 : (int)s;
}
#if 0
static const char * const plaintext[] = {
"any carnal pleasure.",
"any carnal pleasure",
"any carnal pleasur",
"any carnal pleasu",
"any carnal pleas",
"Admin:kloikloi"
};
static const char * const coded[] = {
"YW55IGNhcm5hbCBwbGVhc3VyZS4=",
"YW55IGNhcm5hbCBwbGVhc3VyZQ==",
"YW55IGNhcm5hbCBwbGVhc3Vy",
"YW55IGNhcm5hbCBwbGVhc3U=",
"YW55IGNhcm5hbCBwbGVhcw==",
"QWRtaW46a2xvaWtsb2k="
};
int
lws_b64_selftest(void)
{
char buf[64];
unsigned int n, r = 0;
unsigned int test;
lwsl_notice("%s\n", __func__);
/* examples from https://en.wikipedia.org/wiki/Base64 */
for (test = 0; test < (int)LWS_ARRAY_SIZE(plaintext); test++) {
buf[sizeof(buf) - 1] = '\0';
n = lws_b64_encode_string(plaintext[test],
strlen(plaintext[test]), buf, sizeof buf);
if (n != strlen(coded[test]) || strcmp(buf, coded[test])) {
lwsl_err("Failed lws_b64 encode selftest "
"%d result '%s' %d\n", test, buf, n);
r = -1;
}
buf[sizeof(buf) - 1] = '\0';
n = lws_b64_decode_string(coded[test], buf, sizeof buf);
if (n != strlen(plaintext[test]) ||
strcmp(buf, plaintext[test])) {
lwsl_err("Failed lws_b64 decode selftest "
"%d result '%s' / '%s', %d / %zu\n",
test, buf, plaintext[test], n,
strlen(plaintext[test]));
lwsl_hexdump_err(buf, n);
r = -1;
}
}
if (!r)
lwsl_notice("Base 64 selftests passed\n");
else
lwsl_notice("Base64 selftests failed\n");
return r;
}
#endif

View File

@ -0,0 +1,960 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Implements a cache backing store compatible with netscape cookies.txt format
* There is one entry per "line", and fields are tab-delimited
*
* We need to know the format here, because while the unique cookie tag consists
* of "hostname|urlpath|cookiename", that does not appear like that in the file;
* we have to go parse the fields and synthesize the corresponding tag.
*
* We rely on all the fields except the cookie value fitting in a 256 byte
* buffer, and allow eating multiple buffers to get a huge cookie values.
*
* Because the cookie file is a device-wide asset, although lws will change it
* from the lws thread without conflict, there may be other processes that will
* change it by removal and regenerating the file asynchronously. For that
* reason, file handles are opened fresh each time we want to use the file, so
* we always get the latest version.
*
* When updating the file ourselves, we use a lockfile to ensure our process
* has exclusive access.
*
*
* Tag Matching rules
*
* There are three kinds of tag matching rules
*
* 1) specific - tag strigs must be the same
* 2) wilcard - tags matched using optional wildcards
* 3) wildcard + lookup - wildcard, but path part matches using cookie scope rules
*
*/
#include <private-lib-core.h>
#include "private-lib-misc-cache-ttl.h"
typedef enum nsc_iterator_ret {
NIR_CONTINUE = 0,
NIR_FINISH_OK = 1,
NIR_FINISH_ERROR = -1
} nsc_iterator_ret_t;
typedef enum cbreason {
LCN_SOL = (1 << 0),
LCN_EOL = (1 << 1)
} cbreason_t;
typedef int (*nsc_cb_t)(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
const char *buf, size_t size);
static void
expiry_cb(lws_sorted_usec_list_t *sul);
static int
nsc_backing_open_lock(lws_cache_nscookiejar_t *cache, int mode, const char *par)
{
int sanity = 50;
char lock[128];
int fd_lock, fd;
lwsl_debug("%s: %s\n", __func__, par);
lws_snprintf(lock, sizeof(lock), "%s.LCK",
cache->cache.info.u.nscookiejar.filepath);
do {
fd_lock = open(lock, LWS_O_CREAT | O_EXCL, 0600);
if (fd_lock >= 0) {
close(fd_lock);
break;
}
if (!sanity--) {
lwsl_warn("%s: unable to lock %s: errno %d\n", __func__,
lock, errno);
return -1;
}
#if defined(WIN32)
Sleep(100);
#else
usleep(100000);
#endif
} while (1);
fd = open(cache->cache.info.u.nscookiejar.filepath,
LWS_O_CREAT | mode, 0600);
if (fd == -1) {
lwsl_warn("%s: unable to open or create %s\n", __func__,
cache->cache.info.u.nscookiejar.filepath);
unlink(lock);
}
return fd;
}
static void
nsc_backing_close_unlock(lws_cache_nscookiejar_t *cache, int fd)
{
char lock[128];
lwsl_debug("%s\n", __func__);
lws_snprintf(lock, sizeof(lock), "%s.LCK",
cache->cache.info.u.nscookiejar.filepath);
if (fd >= 0)
close(fd);
unlink(lock);
}
/*
* We're going to call the callback with chunks of the file with flags
* indicating we're giving it the start of a line and / or giving it the end
* of a line.
*
* It's like this because the cookie value may be huge (and to a lesser extent
* the path may also be big).
*
* If it's the start of a line (flags on the cb has LCN_SOL), then the buffer
* contains up to the first 256 chars of the line, it's enough to match with.
*
* We cannot hold the file open inbetweentimes, since other processes may
* regenerate it, so we need to bind to a new inode. We open it with an
* exclusive flock() so other processes can't replace conflicting changes
* while we also write changes, without having to wait and see our changes.
*/
static int
nscookiejar_iterate(lws_cache_nscookiejar_t *cache, int fd,
nsc_cb_t cb, void *opaque)
{
int m = 0, n = 0, e, r = LCN_SOL, ignore = 0, ret = 0;
char temp[256], eof = 0;
if (lseek(fd, 0, SEEK_SET) == (off_t)-1)
return -1;
do { /* for as many buffers in the file */
int n1;
lwsl_debug("%s: n %d, m %d\n", __func__, n, m);
read:
n1 = (int)read(fd, temp + n, sizeof(temp) - (size_t)n);
lwsl_debug("%s: n1 %d\n", __func__, n1);
if (n1 <= 0) {
eof = 1;
if (m == n)
continue;
} else
n += n1;
while (m < n) {
m++;
if (temp[m - 1] != '\n')
continue;
/* ie, we hit EOL */
if (temp[0] == '#')
/* lines starting with # are comments */
e = 0;
else
e = cb(cache, opaque, r | LCN_EOL, temp,
(size_t)m - 1);
r = LCN_SOL;
ignore = 0;
/*
* Move back remainder and prefill the gap that opened
* up: we want to pass enough in the start chunk so the
* cb can classify it even if it can't get all the
* value part in one go
*/
memmove(temp, temp + m, (size_t)(n - m));
n -= m;
m = 0;
if (e) {
ret = e;
goto bail;
}
goto read;
}
if (m) {
/* we ran out of buffer */
if (ignore || (r == LCN_SOL && n && temp[0] == '#')) {
e = 0;
ignore = 1;
} else {
e = cb(cache, opaque,
r | (n == m && eof ? LCN_EOL : 0),
temp, (size_t)m);
m = 0;
n = 0;
}
if (e) {
/*
* We have to call off the whole thing if any
* step, eg, OOMs
*/
ret = e;
goto bail;
}
r = 0;
}
} while (!eof || n != m);
ret = 0;
bail:
return ret;
}
/*
* lookup() just handles wildcard resolution, it doesn't deal with moving the
* hits to L1. That has to be done individually by non-wildcard names.
*/
enum {
NSC_COL_HOST = 0, /* wc idx 0 */
NSC_COL_PATH = 2, /* wc idx 1 */
NSC_COL_EXPIRY = 4,
NSC_COL_NAME = 5, /* wc idx 2 */
NSC_COL_COUNT = 6
};
/*
* This performs the specialized wildcard that knows about cookie path match
* rules.
*
* To defeat the lookup path matching, lie to it about idx being NSC_COL_PATH
*/
static int
nsc_match(const char *wc, size_t wc_len, const char *col, size_t col_len,
int idx)
{
size_t n = 0;
if (idx != NSC_COL_PATH)
return lws_strcmp_wildcard(wc, wc_len, col, col_len);
/*
* Cookie path match is special, if we lookup on a path like /my/path,
* we must match on cookie paths for every dir level including /, so
* match on /, /my, and /my/path. But we must not match on /m or
* /my/pa etc. If we lookup on /, we must not match /my/path
*
* Let's go through wc checking at / and for every complete subpath if
* it is an explicit match
*/
if (!strcmp(col, wc))
return 0; /* exact hit */
while (n <= wc_len) {
if (n == wc_len || wc[n] == '/') {
if (n && col_len <= n && !strncmp(wc, col, n))
return 0; /* hit */
if (n != wc_len && col_len <= n + 1 &&
!strncmp(wc, col, n + 1)) /* check for trailing / */
return 0; /* hit */
}
n++;
}
return 1; /* fail */
}
static const uint8_t nsc_cols[] = { NSC_COL_HOST, NSC_COL_PATH, NSC_COL_NAME };
static int
lws_cache_nscookiejar_tag_match(struct lws_cache_ttl_lru *cache,
const char *wc, const char *tag, char lookup)
{
const char *wc_end = wc + strlen(wc), *tag_end = tag + strlen(tag),
*start_wc, *start_tag;
int n = 0;
lwsl_cache("%s: '%s' vs '%s'\n", __func__, wc, tag);
/*
* Given a well-formed host|path|name tag and a wildcard term,
* make the determination if the tag matches the wildcard or not,
* using lookup rules that apply at this cache level.
*/
while (n < 3) {
start_wc = wc;
while (wc < wc_end && *wc != LWSCTAG_SEP)
wc++;
start_tag = tag;
while (tag < tag_end && *tag != LWSCTAG_SEP)
tag++;
lwsl_cache("%s: '%.*s' vs '%.*s'\n", __func__,
lws_ptr_diff(wc, start_wc), start_wc,
lws_ptr_diff(tag, start_tag), start_tag);
if (nsc_match(start_wc, lws_ptr_diff_size_t(wc, start_wc),
start_tag, lws_ptr_diff_size_t(tag, start_tag),
lookup ? nsc_cols[n] : NSC_COL_HOST)) {
lwsl_cache("%s: fail\n", __func__);
return 1;
}
if (wc < wc_end)
wc++;
if (tag < tag_end)
tag++;
n++;
}
lwsl_cache("%s: hit\n", __func__);
return 0; /* match */
}
/*
* Converts the start of a cookie file line into a tag
*/
static int
nsc_line_to_tag(const char *buf, size_t size, char *tag, size_t max_tag,
lws_usec_t *pexpiry)
{
int n, idx = 0, tl = 0;
lws_usec_t expiry = 0;
size_t bn = 0;
char col[64];
if (size < 3)
return 1;
while (bn < size && idx <= NSC_COL_NAME) {
n = 0;
while (bn < size && n < (int)sizeof(col) - 1 &&
buf[bn] != '\t')
col[n++] = buf[bn++];
col[n] = '\0';
if (buf[bn] == '\t')
bn++;
switch (idx) {
case NSC_COL_EXPIRY:
expiry = (lws_usec_t)((unsigned long long)atoll(col) *
(lws_usec_t)LWS_US_PER_SEC);
break;
case NSC_COL_HOST:
case NSC_COL_PATH:
case NSC_COL_NAME:
/*
* As we match the pieces of the wildcard,
* compose the matches into a specific tag
*/
if (tl + n + 2 > (int)max_tag)
return 1;
if (tl)
tag[tl++] = LWSCTAG_SEP;
memcpy(tag + tl, col, (size_t)n);
tl += n;
tag[tl] = '\0';
break;
default:
break;
}
idx++;
}
if (pexpiry)
*pexpiry = expiry;
lwsl_info("%s: %.*s: tag '%s'\n", __func__, (int)size, buf, tag);
return 0;
}
struct nsc_lookup_ctx {
const char *wildcard_key;
lws_dll2_owner_t *results_owner;
lws_cache_match_t *match; /* current match if any */
size_t wklen;
};
static int
nsc_lookup_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
const char *buf, size_t size)
{
struct nsc_lookup_ctx *ctx = (struct nsc_lookup_ctx *)opaque;
lws_usec_t expiry;
char tag[200];
int tl;
if (!(flags & LCN_SOL)) {
if (ctx->match)
ctx->match->payload_size += size;
return NIR_CONTINUE;
}
/*
* There should be enough in buf to match or reject it... let's
* synthesize a tag from the text "line" and then check the tags for
* a match
*/
ctx->match = NULL; /* new SOL means stop tracking payload len */
if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &expiry))
return NIR_CONTINUE;
if (lws_cache_nscookiejar_tag_match(&cache->cache,
ctx->wildcard_key, tag, 1))
return NIR_CONTINUE;
tl = (int)strlen(tag);
/*
* ... it looks like a match then... create new match
* object with the specific tag, and add it to the owner list
*/
ctx->match = lws_fi(&cache->cache.info.cx->fic, "cache_lookup_oom") ? NULL :
lws_malloc(sizeof(*ctx->match) + (unsigned int)tl + 1u,
__func__);
if (!ctx->match)
/* caller of lookup will clean results list on fail */
return NIR_FINISH_ERROR;
ctx->match->payload_size = size;
ctx->match->tag_size = (size_t)tl;
ctx->match->expiry = expiry;
memset(&ctx->match->list, 0, sizeof(ctx->match->list));
memcpy(&ctx->match[1], tag, (size_t)tl + 1u);
lws_dll2_add_tail(&ctx->match->list, ctx->results_owner);
return NIR_CONTINUE;
}
static int
lws_cache_nscookiejar_lookup(struct lws_cache_ttl_lru *_c,
const char *wildcard_key,
lws_dll2_owner_t *results_owner)
{
lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
struct nsc_lookup_ctx ctx;
int ret, fd;
fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
if (fd < 0)
return 1;
ctx.wildcard_key = wildcard_key;
ctx.results_owner = results_owner;
ctx.wklen = strlen(wildcard_key);
ctx.match = 0;
ret = nscookiejar_iterate(cache, fd, nsc_lookup_cb, &ctx);
/*
* The cb can fail, eg, with OOM, making the whole lookup
* invalid and returning fail. Caller will clean
* results_owner on fail.
*/
nsc_backing_close_unlock(cache, fd);
return ret == NIR_FINISH_ERROR;
}
/*
* It's pretty horrible having to implement add or remove individual items by
* file regeneration, but if we don't want to keep it all in heap, and we want
* this cookie jar format, that is what we are into.
*
* Allow to optionally add a "line", optionally wildcard delete tags, and always
* delete expired entries.
*
* Although we can rely on the lws thread to be doing this, multiple processes
* may be using the cookie jar and can tread on each other. So we use flock()
* (linux only) to get exclusive access while we are processing this.
*
* We leave the existing file alone and generate a new one alongside it, with a
* fixed name.tmp format so it can't leak, if that went OK then we unlink the
* old and rename the new.
*/
struct nsc_regen_ctx {
const char *wildcard_key_delete;
const void *add_data;
lws_usec_t curr;
size_t add_size;
int fdt;
char drop;
};
/* only used by nsc_regen() */
static int
nsc_regen_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
const char *buf, size_t size)
{
struct nsc_regen_ctx *ctx = (struct nsc_regen_ctx *)opaque;
char tag[256];
lws_usec_t expiry;
if (flags & LCN_SOL) {
ctx->drop = 0;
if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &expiry))
/* filter it out if it is unparseable */
goto drop;
/* routinely track the earliest expiry */
if (!cache->earliest_expiry ||
(expiry && cache->earliest_expiry > expiry))
cache->earliest_expiry = expiry;
if (expiry && expiry < ctx->curr)
/* routinely strip anything beyond its expiry */
goto drop;
if (ctx->wildcard_key_delete)
lwsl_cache("%s: %s vs %s\n", __func__,
tag, ctx->wildcard_key_delete);
if (ctx->wildcard_key_delete &&
!lws_cache_nscookiejar_tag_match(&cache->cache,
ctx->wildcard_key_delete,
tag, 0)) {
lwsl_cache("%s: %s matches wc delete %s\n", __func__,
tag, ctx->wildcard_key_delete);
goto drop;
}
}
if (ctx->drop)
return 0;
cache->cache.current_footprint += (uint64_t)size;
if (write(ctx->fdt, buf, /*msvc*/(unsigned int)size) != (ssize_t)size)
return NIR_FINISH_ERROR;
if (flags & LCN_EOL)
if ((size_t)write(ctx->fdt, "\n", 1) != 1)
return NIR_FINISH_ERROR;
return 0;
drop:
ctx->drop = 1;
return NIR_CONTINUE;
}
static int
nsc_regen(lws_cache_nscookiejar_t *cache, const char *wc_delete,
const void *pay, size_t pay_size)
{
struct nsc_regen_ctx ctx;
char filepath[128];
int fd, ret = 1;
fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
if (fd < 0)
return 1;
lws_snprintf(filepath, sizeof(filepath), "%s.tmp",
cache->cache.info.u.nscookiejar.filepath);
unlink(filepath);
if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_temp_open"))
goto bail;
ctx.fdt = open(filepath, LWS_O_CREAT | LWS_O_WRONLY, 0600);
if (ctx.fdt < 0)
goto bail;
/* magic header */
if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_temp_write") ||
/* other consumers insist to see this at start of cookie jar */
write(ctx.fdt, "# Netscape HTTP Cookie File\n", 28) != 28)
goto bail1;
/* if we are adding something, put it first */
if (pay &&
write(ctx.fdt, pay, /*msvc*/(unsigned int)pay_size) !=
(ssize_t)pay_size)
goto bail1;
if (pay && write(ctx.fdt, "\n", 1u) != (ssize_t)1)
goto bail1;
cache->cache.current_footprint = 0;
ctx.wildcard_key_delete = wc_delete;
ctx.add_data = pay;
ctx.add_size = pay_size;
ctx.curr = lws_now_usecs();
ctx.drop = 0;
cache->earliest_expiry = 0;
if (lws_fi(&cache->cache.info.cx->fic, "cache_regen_iter_fail") ||
nscookiejar_iterate(cache, fd, nsc_regen_cb, &ctx))
goto bail1;
close(ctx.fdt);
ctx.fdt = -1;
if (unlink(cache->cache.info.u.nscookiejar.filepath) == -1)
lwsl_info("%s: unlink %s failed\n", __func__,
cache->cache.info.u.nscookiejar.filepath);
if (rename(filepath, cache->cache.info.u.nscookiejar.filepath) == -1)
lwsl_info("%s: rename %s failed\n", __func__,
cache->cache.info.u.nscookiejar.filepath);
if (cache->earliest_expiry)
lws_cache_schedule(&cache->cache, expiry_cb,
cache->earliest_expiry);
ret = 0;
goto bail;
bail1:
if (ctx.fdt >= 0)
close(ctx.fdt);
bail:
unlink(filepath);
nsc_backing_close_unlock(cache, fd);
return ret;
}
static void
expiry_cb(lws_sorted_usec_list_t *sul)
{
lws_cache_nscookiejar_t *cache = lws_container_of(sul,
lws_cache_nscookiejar_t, cache.sul);
/*
* regen the cookie jar without changes, so expired are removed and
* new earliest expired computed
*/
if (nsc_regen(cache, NULL, NULL, 0))
return;
if (cache->earliest_expiry)
lws_cache_schedule(&cache->cache, expiry_cb,
cache->earliest_expiry);
}
/* specific_key and expiry are ignored, since it must be encoded in payload */
static int
lws_cache_nscookiejar_write(struct lws_cache_ttl_lru *_c,
const char *specific_key, const uint8_t *source,
size_t size, lws_usec_t expiry, void **ppvoid)
{
lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
char tag[128];
lwsl_cache("%s: %s: len %d\n", __func__, _c->info.name, (int)size);
assert(source);
if (nsc_line_to_tag((const char *)source, size, tag, sizeof(tag), NULL))
return 1;
if (ppvoid)
*ppvoid = NULL;
if (nsc_regen(cache, tag, source, size)) {
lwsl_err("%s: regen failed\n", __func__);
return 1;
}
return 0;
}
struct nsc_get_ctx {
struct lws_buflist *buflist;
const char *specific_key;
const void **pdata;
size_t *psize;
lws_cache_ttl_lru_t *l1;
lws_usec_t expiry;
};
/*
* We're looking for a specific key, if found, we want to make an entry for it
* in L1 and return information about that
*/
static int
nsc_get_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
const char *buf, size_t size)
{
struct nsc_get_ctx *ctx = (struct nsc_get_ctx *)opaque;
char tag[200];
uint8_t *q;
if (ctx->buflist)
goto collect;
if (!(flags & LCN_SOL))
return NIR_CONTINUE;
if (nsc_line_to_tag(buf, size, tag, sizeof(tag), &ctx->expiry)) {
lwsl_err("%s: can't get tag\n", __func__);
return NIR_CONTINUE;
}
lwsl_cache("%s: %s %s\n", __func__, ctx->specific_key, tag);
if (strcmp(ctx->specific_key, tag)) {
lwsl_cache("%s: no match\n", __func__);
return NIR_CONTINUE;
}
/* it's a match */
lwsl_cache("%s: IS match\n", __func__);
if (!(flags & LCN_EOL))
goto collect;
/* it all fit in the buffer, let's create it in L1 now */
*ctx->psize = size;
if (ctx->l1->info.ops->write(ctx->l1,
ctx->specific_key, (const uint8_t *)buf,
size, ctx->expiry, (void **)ctx->pdata))
return NIR_FINISH_ERROR;
return NIR_FINISH_OK;
collect:
/*
* it's bigger than one buffer-load, we have to stash what we're getting
* on a buflist and create it when we have it all
*/
if (lws_buflist_append_segment(&ctx->buflist, (const uint8_t *)buf,
size))
goto cleanup;
if (!(flags & LCN_EOL))
return NIR_CONTINUE;
/* we have all the payload, create the L1 entry without payload yet */
*ctx->psize = size;
if (ctx->l1->info.ops->write(ctx->l1, ctx->specific_key, NULL,
lws_buflist_total_len(&ctx->buflist),
ctx->expiry, (void **)&q))
goto cleanup;
*ctx->pdata = q;
/* dump the buflist into the L1 cache entry */
do {
uint8_t *p;
size_t len = lws_buflist_next_segment_len(&ctx->buflist, &p);
memcpy(q, p, len);
q += len;
lws_buflist_use_segment(&ctx->buflist, len);
} while (ctx->buflist);
return NIR_FINISH_OK;
cleanup:
lws_buflist_destroy_all_segments(&ctx->buflist);
return NIR_FINISH_ERROR;
}
static int
lws_cache_nscookiejar_get(struct lws_cache_ttl_lru *_c,
const char *specific_key, const void **pdata,
size_t *psize)
{
lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
struct nsc_get_ctx ctx;
int ret, fd;
fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
if (fd < 0)
return 1;
/* get a pointer to l1 */
ctx.l1 = &cache->cache;
while (ctx.l1->child)
ctx.l1 = ctx.l1->child;
ctx.pdata = pdata;
ctx.psize = psize;
ctx.specific_key = specific_key;
ctx.buflist = NULL;
ctx.expiry = 0;
ret = nscookiejar_iterate(cache, fd, nsc_get_cb, &ctx);
nsc_backing_close_unlock(cache, fd);
return ret != NIR_FINISH_OK;
}
static int
lws_cache_nscookiejar_invalidate(struct lws_cache_ttl_lru *_c,
const char *wc_key)
{
lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
return nsc_regen(cache, wc_key, NULL, 0);
}
static struct lws_cache_ttl_lru *
lws_cache_nscookiejar_create(const struct lws_cache_creation_info *info)
{
lws_cache_nscookiejar_t *cache;
cache = lws_fi(&info->cx->fic, "cache_createfail") ? NULL :
lws_zalloc(sizeof(*cache), __func__);
if (!cache)
return NULL;
cache->cache.info = *info;
/*
* We need to scan the file, if it exists, and find the earliest
* expiry while cleaning out any expired entries
*/
expiry_cb(&cache->cache.sul);
lwsl_notice("%s: create %s\n", __func__, info->name ? info->name : "?");
return (struct lws_cache_ttl_lru *)cache;
}
static int
lws_cache_nscookiejar_expunge(struct lws_cache_ttl_lru *_c)
{
lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
int r;
if (!cache)
return 0;
r = unlink(cache->cache.info.u.nscookiejar.filepath);
if (r)
lwsl_warn("%s: failed to unlink %s\n", __func__,
cache->cache.info.u.nscookiejar.filepath);
return r;
}
static void
lws_cache_nscookiejar_destroy(struct lws_cache_ttl_lru **_pc)
{
lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)*_pc;
if (!cache)
return;
lws_sul_cancel(&cache->cache.sul);
lws_free_set_NULL(*_pc);
}
#if defined(_DEBUG)
static int
nsc_dump_cb(lws_cache_nscookiejar_t *cache, void *opaque, int flags,
const char *buf, size_t size)
{
lwsl_hexdump_cache(buf, size);
return 0;
}
static void
lws_cache_nscookiejar_debug_dump(struct lws_cache_ttl_lru *_c)
{
lws_cache_nscookiejar_t *cache = (lws_cache_nscookiejar_t *)_c;
int fd = nsc_backing_open_lock(cache, LWS_O_RDONLY, __func__);
if (fd < 0)
return;
lwsl_cache("%s: %s\n", __func__, _c->info.name);
nscookiejar_iterate(cache, fd, nsc_dump_cb, NULL);
nsc_backing_close_unlock(cache, fd);
}
#endif
const struct lws_cache_ops lws_cache_ops_nscookiejar = {
.create = lws_cache_nscookiejar_create,
.destroy = lws_cache_nscookiejar_destroy,
.expunge = lws_cache_nscookiejar_expunge,
.write = lws_cache_nscookiejar_write,
.tag_match = lws_cache_nscookiejar_tag_match,
.lookup = lws_cache_nscookiejar_lookup,
.invalidate = lws_cache_nscookiejar_invalidate,
.get = lws_cache_nscookiejar_get,
#if defined(_DEBUG)
.debug_dump = lws_cache_nscookiejar_debug_dump,
#endif
};

View File

@ -0,0 +1,608 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
#include "private-lib-misc-cache-ttl.h"
#if defined(write)
#undef write
#endif
static void
update_sul(lws_cache_ttl_lru_t_heap_t *cache);
static int
lws_cache_heap_invalidate(struct lws_cache_ttl_lru *_c, const char *key);
static int
sort_expiry(const lws_dll2_t *a, const lws_dll2_t *b)
{
const lws_cache_ttl_item_heap_t
*c = lws_container_of(a, lws_cache_ttl_item_heap_t, list_expiry),
*d = lws_container_of(b, lws_cache_ttl_item_heap_t, list_expiry);
if (c->expiry > d->expiry)
return 1;
if (c->expiry < d->expiry)
return -1;
return 0;
}
static void
_lws_cache_heap_item_destroy(lws_cache_ttl_lru_t_heap_t *cache,
lws_cache_ttl_item_heap_t *item)
{
lwsl_cache("%s: %s (%s)\n", __func__, cache->cache.info.name,
(const char *)&item[1] + item->size);
lws_dll2_remove(&item->list_expiry);
lws_dll2_remove(&item->list_lru);
cache->cache.current_footprint -= item->size;
update_sul(cache);
if (cache->cache.info.cb)
cache->cache.info.cb((void *)((uint8_t *)&item[1]), item->size);
lws_free(item);
}
static void
lws_cache_heap_item_destroy(lws_cache_ttl_lru_t_heap_t *cache,
lws_cache_ttl_item_heap_t *item, int parent_too)
{
struct lws_cache_ttl_lru *backing = &cache->cache;
const char *tag = ((const char *)&item[1]) + item->size;
/*
* We're destroying a normal item?
*/
if (*tag == META_ITEM_LEADING)
/* no, nothing to check here then */
goto post;
if (backing->info.parent)
backing = backing->info.parent;
/*
* We need to check any cached meta-results from lookups that
* include this normal item, and if any, invalidate the meta-results
* since they have to be recalculated before being used again.
*/
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
cache->items_lru.head) {
lws_cache_ttl_item_heap_t *i = lws_container_of(d,
lws_cache_ttl_item_heap_t,
list_lru);
const char *iname = ((const char *)&item[1]) + item->size;
uint8_t *pay = (uint8_t *)&item[1], *end = pay + item->size;
if (*iname == META_ITEM_LEADING) {
size_t taglen = strlen(iname);
/*
* If the item about to be destroyed makes an
* appearance on the meta results list, we must kill
* the meta result item to force recalc next time
*/
while (pay < end) {
uint32_t tlen = lws_ser_ru32be(pay + 4);
if (tlen == taglen &&
!strcmp((const char *)pay + 8, iname)) {
#if defined(_DEBUG)
/*
* Sanity check that the item tag is
* really a match for that meta results
* item
*/
assert (!backing->info.ops->tag_match(
backing, iname + 1, tag, 1));
#endif
_lws_cache_heap_item_destroy(cache, i);
break;
}
pay += 8 + tlen + 1;
}
#if defined(_DEBUG)
/*
* Sanity check that the item tag really isn't a match
* for that meta results item
*/
assert (backing->info.ops->tag_match(backing, iname + 1,
tag, 1));
#endif
}
} lws_end_foreach_dll_safe(d, d1);
post:
_lws_cache_heap_item_destroy(cache, item);
}
static void
lws_cache_item_evict_lru(lws_cache_ttl_lru_t_heap_t *cache)
{
lws_cache_ttl_item_heap_t *ei;
if (!cache->items_lru.head)
return;
ei = lws_container_of(cache->items_lru.head,
lws_cache_ttl_item_heap_t, list_lru);
lws_cache_heap_item_destroy(cache, ei, 0);
}
/*
* We need to weed out expired entries in the backing file
*/
static void
expiry_cb(lws_sorted_usec_list_t *sul)
{
lws_cache_ttl_lru_t_heap_t *cache = lws_container_of(sul,
lws_cache_ttl_lru_t_heap_t, cache.sul);
lws_usec_t now = lws_now_usecs();
lwsl_cache("%s: %s\n", __func__, cache->cache.info.name);
while (cache->items_expiry.head) {
lws_cache_ttl_item_heap_t *item;
item = lws_container_of(cache->items_expiry.head,
lws_cache_ttl_item_heap_t, list_expiry);
if (item->expiry > now)
return;
lws_cache_heap_item_destroy(cache, item, 1);
}
}
/*
* Let's figure out what the earliest next expiry is
*/
static int
earliest_expiry(lws_cache_ttl_lru_t_heap_t *cache, lws_usec_t *pearliest)
{
lws_cache_ttl_item_heap_t *item;
if (!cache->items_expiry.head)
return 1;
item = lws_container_of(cache->items_expiry.head,
lws_cache_ttl_item_heap_t, list_expiry);
*pearliest = item->expiry;
return 0;
}
static void
update_sul(lws_cache_ttl_lru_t_heap_t *cache)
{
lws_usec_t earliest;
/* weed out any newly-expired */
expiry_cb(&cache->cache.sul);
/* figure out the next soonest expiring item */
if (earliest_expiry(cache, &earliest)) {
lws_sul_cancel(&cache->cache.sul);
return;
}
lwsl_debug("%s: setting exp %llu\n", __func__,
(unsigned long long)earliest);
if (earliest)
lws_cache_schedule(&cache->cache, expiry_cb, earliest);
}
static lws_cache_ttl_item_heap_t *
lws_cache_heap_specific(lws_cache_ttl_lru_t_heap_t *cache,
const char *specific_key)
{
lws_start_foreach_dll(struct lws_dll2 *, d, cache->items_lru.head) {
lws_cache_ttl_item_heap_t *item = lws_container_of(d,
lws_cache_ttl_item_heap_t,
list_lru);
const char *iname = ((const char *)&item[1]) + item->size;
if (!strcmp(specific_key, iname))
return item;
} lws_end_foreach_dll(d);
return NULL;
}
static int
lws_cache_heap_tag_match(struct lws_cache_ttl_lru *cache, const char *wc,
const char *tag, char lookup_rules)
{
return lws_strcmp_wildcard(wc, strlen(wc), tag, strlen(tag));
}
static int
lws_cache_heap_lookup(struct lws_cache_ttl_lru *_c, const char *wildcard_key,
lws_dll2_owner_t *results_owner)
{
lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
size_t sklen = strlen(wildcard_key);
lws_start_foreach_dll(struct lws_dll2 *, d, cache->items_lru.head) {
lws_cache_ttl_item_heap_t *item = lws_container_of(d,
lws_cache_ttl_item_heap_t,
list_lru);
const char *iname = ((const char *)&item[1]) + item->size;
if (!lws_strcmp_wildcard(wildcard_key, sklen, iname,
strlen(iname))) {
size_t ilen = strlen(iname);
lws_cache_match_t *m;
char hit = 0;
/*
* It musn't already be on the list from an earlier
* cache level
*/
lws_start_foreach_dll(struct lws_dll2 *, e,
results_owner->head) {
lws_cache_match_t *i = lws_container_of(e,
lws_cache_match_t, list);
if (i->tag_size == ilen &&
!strcmp(iname, ((const char *)&i[1]))) {
hit = 1;
break;
}
} lws_end_foreach_dll(e);
if (!hit) {
/*
* it's unique, instantiate a record for it
*/
m = lws_fi(&_c->info.cx->fic,
"cache_lookup_oom") ? NULL :
lws_malloc(sizeof(*m) + ilen + 1,
__func__);
if (!m) {
lws_cache_clear_matches(results_owner);
return 1;
}
memset(&m->list, 0, sizeof(m->list));
m->tag_size = ilen;
memcpy(&m[1], iname, ilen + 1);
lws_dll2_add_tail(&m->list, results_owner);
}
}
} lws_end_foreach_dll(d);
return 0;
}
static int
lws_cache_heap_write(struct lws_cache_ttl_lru *_c, const char *specific_key,
const uint8_t *source, size_t size, lws_usec_t expiry,
void **ppvoid)
{
lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
struct lws_cache_ttl_lru *backing = _c;
lws_cache_ttl_item_heap_t *item, *ei;
size_t kl = strlen(specific_key);
char *p;
lwsl_cache("%s: %s: len %d\n", __func__, _c->info.name, (int)size);
/*
* Is this new tag going to invalidate any existing cached meta-results?
*
* If so, let's destroy any of those first to recover the heap
*/
if (backing->info.parent)
backing = backing->info.parent;
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
cache->items_lru.head) {
lws_cache_ttl_item_heap_t *i = lws_container_of(d,
lws_cache_ttl_item_heap_t,
list_lru);
const char *iname = ((const char *)&i[1]) + i->size;
if (*iname == META_ITEM_LEADING) {
/*
* If the item about to be added would match any cached
* results from before it was added, we have to
* invalidate them. To check this, we have to use the
* matching rules at the backing store level
*/
if (!strcmp(iname + 1, specific_key))
_lws_cache_heap_item_destroy(cache, i);
}
} lws_end_foreach_dll_safe(d, d1);
/*
* Keep us under the limit if possible... note this will always allow
* caching a single large item even if it is above the limits
*/
while ((cache->cache.info.max_footprint &&
cache->cache.current_footprint + size >
cache->cache.info.max_footprint) ||
(cache->cache.info.max_items &&
cache->items_lru.count + 1 > cache->cache.info.max_items))
lws_cache_item_evict_lru(cache);
/* remove any existing entry of the same key */
lws_cache_heap_invalidate(&cache->cache, specific_key);
item = lws_fi(&_c->info.cx->fic, "cache_write_oom") ? NULL :
lws_malloc(sizeof(*item) + kl + 1u + size, __func__);
if (!item)
return 1;
cache->cache.current_footprint += item->size;
/* only need to zero down our item object */
memset(item, 0, sizeof(*item));
p = (char *)&item[1];
if (ppvoid)
*ppvoid = p;
/* copy the payload into place */
if (source)
memcpy(p, source, size);
/* copy the key string into place, with terminating NUL */
memcpy(p + size, specific_key, kl + 1);
item->expiry = expiry;
item->key_len = kl;
item->size = size;
if (expiry) {
/* adding to expiry is optional, on nonzero expiry */
lws_dll2_add_sorted(&item->list_expiry, &cache->items_expiry,
sort_expiry);
ei = lws_container_of(cache->items_expiry.head,
lws_cache_ttl_item_heap_t, list_expiry);
lwsl_debug("%s: setting exp %llu\n", __func__,
(unsigned long long)ei->expiry);
lws_cache_schedule(&cache->cache, expiry_cb, ei->expiry);
}
/* always add outselves to head of lru list */
lws_dll2_add_head(&item->list_lru, &cache->items_lru);
return 0;
}
static int
lws_cache_heap_get(struct lws_cache_ttl_lru *_c, const char *specific_key,
const void **pdata, size_t *psize)
{
lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
lws_cache_ttl_item_heap_t *item;
item = lws_cache_heap_specific(cache, specific_key);
if (!item)
return 1;
/* we are using it, move it to lru head */
lws_dll2_remove(&item->list_lru);
lws_dll2_add_head(&item->list_lru, &cache->items_lru);
if (pdata) {
*pdata = (const void *)&item[1];
*psize = item->size;
}
return 0;
}
static int
lws_cache_heap_invalidate(struct lws_cache_ttl_lru *_c, const char *specific_key)
{
lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
struct lws_cache_ttl_lru *backing = _c;
lws_cache_ttl_item_heap_t *item;
const void *user;
size_t size;
if (lws_cache_heap_get(_c, specific_key, &user, &size))
return 0;
if (backing->info.parent)
backing = backing->info.parent;
item = (lws_cache_ttl_item_heap_t *)(((uint8_t *)user) - sizeof(*item));
/*
* We must invalidate any cached results that would have included this
*/
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
cache->items_lru.head) {
lws_cache_ttl_item_heap_t *i = lws_container_of(d,
lws_cache_ttl_item_heap_t,
list_lru);
const char *iname = ((const char *)&i[1]) + i->size;
if (*iname == META_ITEM_LEADING) {
/*
* If the item about to be added would match any cached
* results from before it was added, we have to
* invalidate them. To check this, we have to use the
* matching rules at the backing store level
*/
if (!backing->info.ops->tag_match(backing, iname + 1,
specific_key, 1))
_lws_cache_heap_item_destroy(cache, i);
}
} lws_end_foreach_dll_safe(d, d1);
lws_cache_heap_item_destroy(cache, item, 0);
return 0;
}
static struct lws_cache_ttl_lru *
lws_cache_heap_create(const struct lws_cache_creation_info *info)
{
lws_cache_ttl_lru_t_heap_t *cache;
assert(info->cx);
assert(info->name);
cache = lws_fi(&info->cx->fic, "cache_createfail") ? NULL :
lws_zalloc(sizeof(*cache), __func__);
if (!cache)
return NULL;
cache->cache.info = *info;
if (info->parent)
info->parent->child = &cache->cache;
// lwsl_cache("%s: create %s\n", __func__, info->name);
return (struct lws_cache_ttl_lru *)cache;
}
static int
destroy_dll(struct lws_dll2 *d, void *user)
{
lws_cache_ttl_lru_t *_c = (struct lws_cache_ttl_lru *)user;
lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
lws_cache_ttl_item_heap_t *item =
lws_container_of(d, lws_cache_ttl_item_heap_t, list_lru);
lws_cache_heap_item_destroy(cache, item, 0);
return 0;
}
static int
lws_cache_heap_expunge(struct lws_cache_ttl_lru *_c)
{
lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
lws_dll2_foreach_safe(&cache->items_lru, cache, destroy_dll);
return 0;
}
static void
lws_cache_heap_destroy(struct lws_cache_ttl_lru **_cache)
{
lws_cache_ttl_lru_t *c = *_cache;
lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)c;
if (!cache)
return;
lws_sul_cancel(&c->sul);
lws_dll2_foreach_safe(&cache->items_lru, cache, destroy_dll);
lws_free_set_NULL(*_cache);
}
#if defined(_DEBUG)
static int
dump_dll(struct lws_dll2 *d, void *user)
{
lws_cache_ttl_item_heap_t *item =
lws_container_of(d, lws_cache_ttl_item_heap_t, list_lru);
lwsl_cache(" %s: size %d, exp %llu\n",
(const char *)&item[1] + item->size,
(int)item->size, (unsigned long long)item->expiry);
lwsl_hexdump_cache((const char *)&item[1], item->size);
return 0;
}
static void
lws_cache_heap_debug_dump(struct lws_cache_ttl_lru *_c)
{
lws_cache_ttl_lru_t_heap_t *cache = (lws_cache_ttl_lru_t_heap_t *)_c;
#if !defined(LWS_WITH_NO_LOGS)
lws_cache_ttl_item_heap_t *item = NULL;
lws_dll2_t *d = cache->items_expiry.head;
if (d)
item = lws_container_of(d, lws_cache_ttl_item_heap_t,
list_expiry);
lwsl_cache("%s: %s: items %d, earliest %llu\n", __func__,
cache->cache.info.name, (int)cache->items_lru.count,
item ? (unsigned long long)item->expiry : 0ull);
#endif
lws_dll2_foreach_safe(&cache->items_lru, cache, dump_dll);
}
#endif
const struct lws_cache_ops lws_cache_ops_heap = {
.create = lws_cache_heap_create,
.destroy = lws_cache_heap_destroy,
.expunge = lws_cache_heap_expunge,
.write = lws_cache_heap_write,
.tag_match = lws_cache_heap_tag_match,
.lookup = lws_cache_heap_lookup,
.invalidate = lws_cache_heap_invalidate,
.get = lws_cache_heap_get,
#if defined(_DEBUG)
.debug_dump = lws_cache_heap_debug_dump,
#endif
};

View File

@ -0,0 +1,300 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <private-lib-core.h>
#include "private-lib-misc-cache-ttl.h"
#include <assert.h>
#if defined(write)
#undef write
#endif
void
lws_cache_clear_matches(lws_dll2_owner_t *results_owner)
{
lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, results_owner->head) {
lws_cache_match_t *item = lws_container_of(d, lws_cache_match_t,
list);
lws_dll2_remove(d);
lws_free(item);
} lws_end_foreach_dll_safe(d, d1);
}
void
lws_cache_schedule(struct lws_cache_ttl_lru *cache, sul_cb_t cb, lws_usec_t e)
{
lwsl_cache("%s: %s schedule %llu\n", __func__, cache->info.name,
(unsigned long long)e);
lws_sul_schedule(cache->info.cx, cache->info.tsi, &cache->sul, cb,
e - lws_now_usecs());
}
int
lws_cache_write_through(struct lws_cache_ttl_lru *cache,
const char *specific_key, const uint8_t *source,
size_t size, lws_usec_t expiry, void **ppay)
{
struct lws_cache_ttl_lru *levels[LWS_CACHE_MAX_LEVELS], *c = cache;
int n = 0, r = 0;
lws_cache_item_remove(cache, specific_key);
/* starting from L1 */
do {
levels[n++] = c;
c = c->info.parent;
} while (c && n < (int)LWS_ARRAY_SIZE(levels));
/* starting from outermost cache level */
while (n) {
n--;
r = levels[n]->info.ops->write(levels[n], specific_key,
source, size, expiry, ppay);
}
return r;
}
/*
* We want to make a list of unique keys that exist at any cache level
* matching a wildcard search key.
*
* If L1 has a cached version though, we will just use that.
*/
int
lws_cache_lookup(struct lws_cache_ttl_lru *cache, const char *wildcard_key,
const void **pdata, size_t *psize)
{
struct lws_cache_ttl_lru *l1 = cache;
lws_dll2_owner_t results_owner;
lws_usec_t expiry = 0;
char meta_key[128];
uint8_t *p, *temp;
size_t sum = 0;
int n;
memset(&results_owner, 0, sizeof(results_owner));
meta_key[0] = META_ITEM_LEADING;
lws_strncpy(&meta_key[1], wildcard_key, sizeof(meta_key) - 2);
/*
* If we have a cached result set in L1 already, return that
*/
if (!l1->info.ops->get(l1, meta_key, pdata, psize))
return 0;
/*
* No, we have to do the actual lookup work in the backing store layer
* to get results for this...
*/
while (cache->info.parent)
cache = cache->info.parent;
if (cache->info.ops->lookup(cache, wildcard_key, &results_owner)) {
/* eg, OOM */
lwsl_cache("%s: bs lookup fail\n", __func__);
lws_cache_clear_matches(&results_owner);
return 1;
}
/*
* Scan the results, we want to know how big a payload it needs in
* the cache, and we want to know the earliest expiry of any of the
* component parts, so the meta cache entry for these results can be
* expired when any of the results would expire.
*/
lws_start_foreach_dll(struct lws_dll2 *, d, results_owner.head) {
lws_cache_match_t *m = lws_container_of(d, lws_cache_match_t,
list);
sum += 8; /* payload size, name length */
sum += m->tag_size + 1;
if (m->expiry && (!expiry || expiry < m->expiry))
expiry = m->expiry;
} lws_end_foreach_dll(d);
lwsl_cache("%s: results %d, size %d\n", __func__,
(int)results_owner.count, (int)sum);
temp = lws_malloc(sum, __func__);
if (!temp) {
lws_cache_clear_matches(&results_owner);
return 1;
}
/*
* Fill temp with the serialized results
*/
p = temp;
lws_start_foreach_dll(struct lws_dll2 *, d, results_owner.head) {
lws_cache_match_t *m = lws_container_of(d, lws_cache_match_t,
list);
/* we don't copy the payload in, but take note of its size */
lws_ser_wu32be(p, (uint32_t)m->payload_size);
p += 4;
/* length of the tag name (there is an uncounted NUL after) */
lws_ser_wu32be(p, (uint32_t)m->tag_size);
p += 4;
/* then the tag name, plus the extra NUL */
memcpy(p, &m[1], m->tag_size + 1);
p += m->tag_size + 1;
} lws_end_foreach_dll(d);
lws_cache_clear_matches(&results_owner);
/*
* Create the right amount of space for an L1 record of these results,
* with its expiry set to the earliest of the results, and copy it in
* from temp
*/
n = l1->info.ops->write(l1, meta_key, temp, sum, expiry, (void **)&p);
/* done with temp */
lws_free(temp);
if (n)
return 1;
/* point to the results in L1 */
*pdata = p;
*psize = sum;
return 0;
}
int
lws_cache_item_get(struct lws_cache_ttl_lru *cache, const char *specific_key,
const void **pdata, size_t *psize)
{
while (cache) {
if (!cache->info.ops->get(cache, specific_key, pdata, psize)) {
lwsl_cache("%s: hit\n", __func__);
return 0;
}
cache = cache->info.parent;
}
return 1;
}
int
lws_cache_expunge(struct lws_cache_ttl_lru *cache)
{
int ret = 0;
while (cache) {
ret |= cache->info.ops->expunge(cache);
cache = cache->info.parent;
}
return ret;
}
int
lws_cache_item_remove(struct lws_cache_ttl_lru *cache, const char *wildcard_key)
{
while (cache) {
if (cache->info.ops->invalidate(cache, wildcard_key))
return 1;
cache = cache->info.parent;
}
return 0;
}
uint64_t
lws_cache_footprint(struct lws_cache_ttl_lru *cache)
{
return cache->current_footprint;
}
void
lws_cache_debug_dump(struct lws_cache_ttl_lru *cache)
{
#if defined(_DEBUG)
if (cache->info.ops->debug_dump)
cache->info.ops->debug_dump(cache);
#endif
}
int
lws_cache_results_walk(lws_cache_results_t *walk_ctx)
{
if (!walk_ctx->size)
return 1;
walk_ctx->payload_len = lws_ser_ru32be(walk_ctx->ptr);
walk_ctx->tag_len = lws_ser_ru32be(walk_ctx->ptr + 4);
walk_ctx->tag = walk_ctx->ptr + 8;
walk_ctx->ptr += walk_ctx->tag_len + 1 + 8;
walk_ctx->size -= walk_ctx->tag_len + 1 + 8;
return 0;
}
struct lws_cache_ttl_lru *
lws_cache_create(const struct lws_cache_creation_info *info)
{
assert(info);
assert(info->ops);
assert(info->name);
assert(info->ops->create);
return info->ops->create(info);
}
void
lws_cache_destroy(struct lws_cache_ttl_lru **_cache)
{
lws_cache_ttl_lru_t *cache = *_cache;
if (!cache)
return;
assert(cache->info.ops->destroy);
lws_sul_cancel(&cache->sul);
cache->info.ops->destroy(_cache);
}

View File

@ -0,0 +1,98 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#define lwsl_cache lwsl_debug
#define lwsl_hexdump_cache lwsl_hexdump_debug
#define LWS_CACHE_MAX_LEVELS 3
/*
* If we need structure inside the cache tag names, use this character as a
* separator
*/
#define LWSCTAG_SEP '|'
/*
* Our synthetic cache result items all have tags starting with this char
*/
#define META_ITEM_LEADING '!'
typedef struct lws_cache_ttl_item_heap {
lws_dll2_t list_expiry;
lws_dll2_t list_lru;
lws_usec_t expiry;
size_t key_len;
size_t size;
/*
* len + key_len + 1 bytes of data overcommitted, user object first
* so it is well-aligned, then the NUL-terminated key name
*/
} lws_cache_ttl_item_heap_t;
/* this is a "base class", all cache implementations have one at the start */
typedef struct lws_cache_ttl_lru {
struct lws_cache_creation_info info;
lws_sorted_usec_list_t sul;
struct lws_cache_ttl_lru *child;
uint64_t current_footprint;
} lws_cache_ttl_lru_t;
/*
* The heap-backed cache uses lws_dll2 linked-lists to track items that are
* in it.
*/
typedef struct lws_cache_ttl_lru_heap {
lws_cache_ttl_lru_t cache;
lws_dll2_owner_t items_expiry;
lws_dll2_owner_t items_lru;
} lws_cache_ttl_lru_t_heap_t;
/*
* We want to be able to work with a large file-backed implementation even on
* devices that don't have heap to track what is in it. It means if lookups
* reach this cache layer, we will be scanning a potentially large file.
*
* L1 caching of lookups (including null result list) reduces the expense of
* this on average. We keep a copy of the last computed earliest expiry.
*
* We can't keep an open file handle here. Because other processes may change
* the cookie file by deleting and replacing it, we have to open it fresh each
* time.
*/
typedef struct lws_cache_nscookiejar {
lws_cache_ttl_lru_t cache;
lws_usec_t earliest_expiry;
} lws_cache_nscookiejar_t;
void
lws_cache_clear_matches(lws_dll2_owner_t *results_owner);
void
lws_cache_schedule(struct lws_cache_ttl_lru *cache, sul_cb_t cb, lws_usec_t e);

View File

@ -0,0 +1,120 @@
azimuth:
background-attachment:
background-color:
background-image:
background-position:
background-repeat:
background:
border-collapse:
border-color:
border-spacing:
border-style:
border-top:
border-right:
border-bottom:
border-left:
border-top-color:
border-right-color:
border-bottom-color:
border-left-color:
border-top-style:
border-right-style:
border-bottom-style:
border-left-style:
border-top-width:
border-right-width:
border-bottom-width:
border-left-width:
border-width:
border-top-left-radius:
border-top-right-radius:
border-bottom-left-radius:
border-bottom-right-radius:
border-radius:
border:
bottom:
caption-side:
clear:
clip:
color:
content:
counter-increment:
counter-reset:
cue-after:
cue-before:
cue:
cursor:
direction:
display:
elevation:
empty-cells:
float:
font-family:
font-size:
font-style:
font-variant:
font-weight:
font:
height:
left:
letter-spacing:
line-height:
list-style-image:
list-style-position:
list-style-type:
list-style:
margin-right:
margin-left:
margin-top:
margin-bottom:
margin:
max-height:
max-width:
min-height:
min-width:
orphans:
outline-color:
outline-style:
outline-width:
outline:
overflow:
padding-top:
padding-right:
padding-bottom:
padding-left:
padding:
page-break-after:
page-break-before:
page-break-inside:
pause-after:
pause-before:
pause:
pitch-range:
pitch:
play-during:
position:
quotes:
richness:
right:
speak-header:
speak-numeral:
speak-punctuation:
speak:
speech-rate:
stress:
table-layout:
text-align:
text-decoration:
text-indent:
text-transform:
top:
unicode-bidi:
vertical-align:
visibility:
voice-family:
volume:
white-space:
widows:
width:
word-spacing:
z-index:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
above
absolute
always
armenian
auto
avoid
baseline
behind
below
bidi-override
blink
block
bold
bolder
both
bottom
capitalize
caption
center
circle
close-quote
code
collapse
continuous
crosshair
decimal-leading-zero
decimal
digits
disc
embed
e-resize
fixed
georgian
help
hidden
hide
high
higher
icon
inherit
inline
inline-block
inline-table
invert
italic
justify
left
lighter
line-through
list-item
low
lower
lower-alpha
lowercase
lower-greek
lower-latin
lower-roman
ltr
menu
message-box
middle
mix
move
ne-resize
no-close-quote
none
no-open-quote
no-repeat
normal
nowrap
n-resize
nw-resize
oblique
once
open-quote
outside
overline
pointer
pre
pre-line
pre-wrap
progress
relative
repeat
repeat-x
repeat-y
right
rtl
scroll
separate
se-resize
show
silent
small-caps
small-caption
spell-out
square
s-resize
static
status-bar
sub
super
sw-resize
table
table-caption
table-cell
table-column
table-column-group
table-footer-group
table-header-group
table-row
table-row-group
text-bottom
text-top
text
top
transparent
underline
upper-alpha
uppercase
upper-latin
upper-roman
visible
wait
w-resize

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,236 @@
/*
* This code is mainly taken from Doug Potter's page
*
* http://www-theorie.physik.unizh.ch/~dpotter/howto/daemonize
*
* I contacted him 2007-04-16 about the license for the original code,
* he replied it is Public Domain. Use the URL above to get the original
* Public Domain version if you want it.
*
* This version is MIT like the rest of libwebsockets and is
* Copyright (c)2006 - 2013 Andy Green <andy@warmcat.com>
*
*
* You're much better advised to use systemd to daemonize stuff without needing
* this kind of support in the app itself.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>
#include <libwebsockets.h>
#include "private-lib-core.h"
pid_t pid_daemon;
static char *lock_path;
pid_t get_daemonize_pid()
{
return pid_daemon;
}
static void
child_handler(int signum)
{
int len, sent, fd;
char sz[20];
switch (signum) {
case SIGALRM: /* timed out daemonizing */
exit(0);
break;
case SIGUSR1: /* positive confirmation we daemonized well */
if (!lock_path)
exit(0);
/* Create the lock file as the current user */
fd = lws_open(lock_path, O_TRUNC | O_RDWR | O_CREAT, 0640);
if (fd < 0) {
fprintf(stderr,
"unable to create lock file %s, code=%d (%s)\n",
lock_path, errno, strerror(errno));
exit(0);
}
len = sprintf(sz, "%u", (unsigned int)pid_daemon);
sent = (int)write(fd, sz, (size_t)len);
if (sent != len)
fprintf(stderr,
"unable to write pid to lock file %s, code=%d (%s)\n",
lock_path, errno, strerror(errno));
close(fd);
exit(0);
//!!(sent == len));
case SIGCHLD: /* daemonization failed */
exit(0);
break;
}
}
static void lws_daemon_closing(int sigact)
{
if (getpid() == pid_daemon)
if (lock_path) {
unlink(lock_path);
lws_free_set_NULL(lock_path);
}
kill(getpid(), SIGKILL);
}
/*
* You just need to call this from your main(), when it
* returns you are all set "in the background" decoupled
* from the console you were started from.
*
* The process context you called from has been terminated then.
*/
int
lws_daemonize(const char *_lock_path)
{
struct sigaction act;
pid_t sid, parent;
/* already a daemon */
// if (getppid() == 1)
// return 1;
if (_lock_path) {
int n;
int fd = lws_open(_lock_path, O_RDONLY);
if (fd >= 0) {
char buf[10];
n = (int)read(fd, buf, sizeof(buf));
close(fd);
if (n) {
int ret;
n = atoi(buf);
ret = kill(n, 0);
if (ret >= 0) {
fprintf(stderr,
"Daemon already running pid %d\n",
n);
exit(1);
}
fprintf(stderr,
"Removing stale lock %s from dead pid %d\n",
_lock_path, n);
unlink(lock_path);
}
}
n = (int)strlen(_lock_path) + 1;
lock_path = lws_malloc((unsigned int)n, "daemonize lock");
if (!lock_path) {
fprintf(stderr, "Out of mem in lws_daemonize\n");
return 1;
}
strcpy(lock_path, _lock_path);
}
/* Trap signals that we expect to receive */
signal(SIGCHLD, child_handler); /* died */
signal(SIGUSR1, child_handler); /* was happy */
signal(SIGALRM, child_handler); /* timeout daemonizing */
/* Fork off the parent process */
pid_daemon = fork();
if ((int)pid_daemon < 0) {
fprintf(stderr, "unable to fork daemon, code=%d (%s)",
errno, strerror(errno));
exit(9);
}
/* If we got a good PID, then we can exit the parent process. */
if (pid_daemon > 0) {
/*
* Wait for confirmation signal from the child via
* SIGCHILD / USR1, or for two seconds to elapse
* (SIGALRM). pause() should not return.
*/
alarm(2);
pause();
/* should not be reachable */
exit(1);
}
/* At this point we are executing as the child process */
parent = getppid();
pid_daemon = getpid();
/* Cancel certain signals */
signal(SIGCHLD, SIG_DFL); /* A child process dies */
signal(SIGTSTP, SIG_IGN); /* Various TTY signals */
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */
/* Change the file mode mask */
umask(0);
/* Create a new SID for the child process */
sid = setsid();
if (sid < 0) {
fprintf(stderr,
"unable to create a new session, code %d (%s)",
errno, strerror(errno));
exit(2);
}
/*
* Change the current working directory. This prevents the current
* directory from being locked; hence not being able to remove it.
*/
if (chdir("/tmp") < 0) {
fprintf(stderr,
"unable to change directory to %s, code %d (%s)",
"/", errno, strerror(errno));
exit(3);
}
/* Redirect standard files to /dev/null */
if (!freopen("/dev/null", "r", stdin))
fprintf(stderr, "unable to freopen() stdin, code %d (%s)",
errno, strerror(errno));
if (!freopen("/dev/null", "w", stdout))
fprintf(stderr, "unable to freopen() stdout, code %d (%s)",
errno, strerror(errno));
if (!freopen("/dev/null", "w", stderr))
fprintf(stderr, "unable to freopen() stderr, code %d (%s)",
errno, strerror(errno));
/* Tell the parent process that we are A-okay */
kill(parent, SIGUSR1);
act.sa_handler = lws_daemon_closing;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGTERM, &act, NULL);
/* return to continue what is now "the daemon" */
return 0;
}

View File

@ -0,0 +1,472 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if !defined(NO_GNU_SOURCE_THIS_TIME)
#define NO_GNU_SOURCE_THIS_TIME
#endif
#if !defined(_DARWIN_C_SOURCE)
#define _DARWIN_C_SOURCE
#endif
#include "private-lib-core.h"
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#if defined(WIN32)
#include <direct.h>
#define read _read
#define open _open
#define close _close
#define write _write
#define mkdir(x,y) _mkdir(x)
#define rmdir _rmdir
#define unlink _unlink
#define HAVE_STRUCT_TIMESPEC
#if defined(pid_t)
#undef pid_t
#endif
#endif /* win32 */
#define COMBO_SIZEOF 512
#if !defined(LWS_PLAT_FREERTOS)
#if defined(WIN32)
#include "../../win32port/dirent/dirent-win32.h"
#else
#include <dirent.h>
#endif
static int filter(const struct dirent *ent)
{
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
return 0;
return 1;
}
#if !defined(WIN32)
static char csep = '/';
#else
static char csep = '\\';
#endif
static void
lws_dir_via_stat(char *combo, size_t l, const char *path, struct lws_dir_entry *lde)
{
struct stat s;
lws_strncpy(combo + l, path, COMBO_SIZEOF - l);
lde->type = LDOT_UNKNOWN;
if (!stat(combo, &s)) {
switch (s.st_mode & S_IFMT) {
case S_IFBLK:
lde->type = LDOT_BLOCK;
break;
case S_IFCHR:
lde->type = LDOT_CHAR;
break;
case S_IFDIR:
lde->type = LDOT_DIR;
break;
case S_IFIFO:
lde->type = LDOT_FIFO;
break;
#if !defined(WIN32)
case S_IFLNK:
lde->type = LDOT_LINK;
break;
#endif
case S_IFREG:
lde->type = LDOT_FILE;
break;
default:
break;
}
}
}
int
lws_dir(const char *dirpath, void *user, lws_dir_callback_function cb)
{
struct lws_dir_entry lde;
struct dirent **namelist;
int n, i, ret = 1;
char combo[COMBO_SIZEOF];
size_t l;
l = (size_t)(ssize_t)lws_snprintf(combo, COMBO_SIZEOF - 2, "%s", dirpath);
combo[l++] = csep;
combo[l] = '\0';
n = scandir((char *)dirpath, &namelist, filter, alphasort);
if (n < 0) {
lwsl_err("Scandir on '%s' failed, errno %d\n", dirpath, LWS_ERRNO);
return 1;
}
for (i = 0; i < n; i++) {
#if !defined(__sun) && !defined(__QNX__)
unsigned int type = namelist[i]->d_type;
#endif
if (strchr(namelist[i]->d_name, '~'))
goto skip;
lde.name = namelist[i]->d_name;
/*
* some filesystems don't report this (ZFS) and tell that
* files are LDOT_UNKNOWN
*/
#if defined(__sun) || defined(__QNX__)
lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde);
#else
/*
* XFS on Linux doesn't fill in d_type at all, always zero.
*/
if (DT_BLK != DT_UNKNOWN && type == DT_BLK)
lde.type = LDOT_BLOCK;
else if (DT_CHR != DT_UNKNOWN && type == DT_CHR)
lde.type = LDOT_CHAR;
else if (DT_DIR != DT_UNKNOWN && type == DT_DIR)
lde.type = LDOT_DIR;
else if (DT_FIFO != DT_UNKNOWN && type == DT_FIFO)
lde.type = LDOT_FIFO;
else if (DT_LNK != DT_UNKNOWN && type == DT_LNK)
lde.type = LDOT_LINK;
else if (DT_REG != DT_UNKNOWN && type == DT_REG)
lde.type = LDOT_FILE;
else if (DT_SOCK != DT_UNKNOWN && type == DT_SOCK)
lde.type = LDOTT_SOCKET;
else {
lde.type = LDOT_UNKNOWN;
lws_dir_via_stat(combo, l, namelist[i]->d_name, &lde);
}
#endif
if (cb(dirpath, user, &lde)) {
while (i < n)
free(namelist[i++]);
ret = 0; /* told to stop by cb */
goto bail;
}
skip:
free(namelist[i]);
}
bail:
free(namelist);
return ret;
}
/*
* Check filename against one globby filter
*
* We can support things like "*.rpm"
*/
static int
lws_dir_glob_check(const char *nm, const char *filt)
{
while (*nm) {
if (*filt == '*') {
if (!strcmp(nm, filt + 1))
return 1;
} else {
if (*nm != *filt)
return 0;
filt++;
}
nm++;
}
return 0;
}
/*
* We get passed a single filter string, like "*.txt" or "mydir/\*.rpm" or so.
*/
int
lws_dir_glob_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
{
lws_dir_glob_t *filter = (lws_dir_glob_t*)user;
char path[384];
if (!strcmp(lde->name, ".") || !strcmp(lde->name, ".."))
return 0;
if (lde->type == LDOT_DIR)
return 0;
if (lws_dir_glob_check(lde->name, filter->filter)) {
lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep,
lde->name);
filter->cb(filter->user, path);
}
return 0;
}
int
lws_dir_rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
{
char path[384];
if (!strcmp(lde->name, ".") || !strcmp(lde->name, ".."))
return 0;
lws_snprintf(path, sizeof(path), "%s%c%s", dirpath, csep, lde->name);
if (lde->type == LDOT_DIR) {
#if !defined(WIN32) && !defined(_WIN32) && !defined(__COVERITY__)
char dummy[8];
/*
* hm... eg, recursive dir symlinks can show up a LDOT_DIR
* here. If it's a symlink, don't recurse into it.
*
* Notice we immediately discard dummy without looking in it.
* There is no way to get into trouble from its lack of NUL
* termination in dummy[]. We just wanted to know if it was
* a symlink at all.
*
* Hide this from Coverity since it flags any use of readlink()
* even if safe.
*/
if (readlink(path, dummy, sizeof(dummy)) < 0)
#endif
lws_dir(path, NULL, lws_dir_rm_rf_cb);
if (rmdir(path))
lwsl_warn("%s: rmdir %s failed %d\n", __func__, path, errno);
} else {
if (unlink(path)) {
#if defined(WIN32)
SetFileAttributesA(path, FILE_ATTRIBUTE_NORMAL);
if (unlink(path))
#else
if (rmdir(path))
#endif
lwsl_warn("%s: unlink %s failed %d (type %d)\n",
__func__, path, errno, lde->type);
}
}
return 0;
}
#endif
#if defined(LWS_WITH_PLUGINS_API)
struct lws_plugin *
lws_plugin_alloc(struct lws_plugin **pplugin)
{
struct lws_plugin *pin = lws_malloc(sizeof(*pin), __func__);
if (!pin)
return NULL;
pin->list = *pplugin;
*pplugin = pin;
return pin;
}
#if defined(LWS_BUILTIN_PLUGIN_NAMES)
extern lws_plugin_protocol_t lws_sshd_demo, lws_ssh_base;
lws_plugin_protocol_t *builtin_pcols[] = {
&lws_sshd_demo, &lws_ssh_base
};
int
lws_plugins_handle_builtin(struct lws_plugin **pplugin,
each_plugin_cb_t each, void *each_user)
{
size_t n = 0;
while (n < LWS_ARRAY_SIZE(builtin_pcols)) {
struct lws_plugin *pin = lws_plugin_alloc(pplugin);
if (!pin)
return 1;
pin->u.l = NULL;
pin->hdr = &builtin_pcols[n]->hdr;
if (each)
each(pin, each_user);
n++;
}
return 0;
}
#endif
struct lws_plugins_args {
struct lws_plugin **pplugin;
const char *_class;
const char *filter;
each_plugin_cb_t each;
void *each_user;
};
static int
lws_plugins_dir_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
{
struct lws_plugins_args *pa = (struct lws_plugins_args *)user;
char path[256], base[64], *q = base;
const lws_plugin_header_t *pl;
const char *p;
if (strlen(lde->name) < 7)
return 0; /* keep going */
/*
* The actual plugin names for protocol plugins look like
* "libprotocol_lws_ssh_base.so" and for event libs
* "libwebsockets-evlib_ev.so"... to recover the base name of
* "lws_ssh_base" and "evlib_ev" we strip from the left to after the
* first _ or -, and then truncate at the first .
*/
p = lde->name;
while (*p && *p != '_' && *p != '-')
p++;
if (!*p)
return 0;
p++;
while (*p && *p != '.' && lws_ptr_diff(q, base) < (int)sizeof(base) - 1)
*q++ = *p++;
*q = '\0';
/* if he's given a filter, only match if base matches it */
if (pa->filter && strcmp(base, pa->filter))
return 0; /* keep going */
lws_snprintf(path, sizeof(path) - 1, "%s/%s", dirpath, lde->name);
pl = lws_plat_dlopen(pa->pplugin, path, base, pa->_class,
pa->each, pa->each_user);
/*
* If we were looking for a specific plugin, finding it should make
* us stop looking (eg, to account for directory precedence of the
* same plugin). If scanning for plugins in a dir, we always keep
* going.
*/
return pa->filter && pl;
}
int
lws_plugins_init(struct lws_plugin **pplugin, const char * const *d,
const char *_class, const char *filter,
each_plugin_cb_t each, void *each_user)
{
struct lws_plugins_args pa;
char *ld_env;
int ret = 1;
pa.pplugin = pplugin;
pa._class = _class;
pa.each = each;
pa.each_user = each_user;
pa.filter = filter;
/*
* Check LD_LIBRARY_PATH override path first if present
*/
ld_env = getenv("LD_LIBRARY_PATH");
if (ld_env) {
char temp[128];
struct lws_tokenize ts;
memset(&ts, 0, sizeof(ts));
ts.start = ld_env;
ts.len = strlen(ld_env);
ts.flags = LWS_TOKENIZE_F_SLASH_NONTERM |
LWS_TOKENIZE_F_DOT_NONTERM |
LWS_TOKENIZE_F_MINUS_NONTERM |
LWS_TOKENIZE_F_NO_INTEGERS |
LWS_TOKENIZE_F_NO_FLOATS;
do {
ts.e = (int8_t)lws_tokenize(&ts);
if (ts.e != LWS_TOKZE_TOKEN)
continue;
lws_strnncpy(temp, ts.token,
ts.token_len,
sizeof(temp));
lwsl_info("%s: trying %s\n", __func__, temp);
if (!lws_dir(temp, &pa, lws_plugins_dir_cb))
ret = 0;
} while (ts.e > 0);
}
while (d && *d) {
lwsl_info("%s: trying %s\n", __func__, *d);
if (!lws_dir(*d, &pa, lws_plugins_dir_cb))
ret = 0;
d++;
}
return ret;
}
int
lws_plugins_destroy(struct lws_plugin **pplugin, each_plugin_cb_t each,
void *each_user)
{
struct lws_plugin *p = *pplugin, *p1;
while (p) {
if (each)
each(p, each_user);
if (p->u.l)
lws_plat_destroy_dl(p);
p1 = p->list;
p->list = NULL;
lws_free(p);
p = p1;
}
*pplugin = NULL;
return 0;
}
#endif

View File

@ -0,0 +1,490 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#include <pthread.h>
#include <libwebsockets.h>
#include "private-lib-core.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#if defined(__APPLE__)
#include <sys/dirent.h>
/* Travis OSX does not have DT_REG... */
#if !defined(DT_REG)
#define DT_REG 8
#endif
#endif
struct file_entry {
lws_list_ptr sorted;
lws_list_ptr prev;
char name[64];
time_t modified;
size_t size;
};
struct lws_diskcache_scan {
struct file_entry *batch;
const char *cache_dir_base;
lws_list_ptr head;
time_t last_scan_completed;
uint64_t agg_size;
uint64_t cache_size_limit;
uint64_t avg_size;
uint64_t cache_tries;
uint64_t cache_hits;
int cache_subdir;
int batch_in_use;
int agg_file_count;
int secs_waiting;
};
#define KIB (1024)
#define MIB (KIB * KIB)
#define lp_to_fe(p, _n) lws_list_ptr_container(p, struct file_entry, _n)
static const char *hex = "0123456789abcdef";
#define BATCH_COUNT 128
static int
fe_modified_sort(lws_list_ptr a, lws_list_ptr b)
{
struct file_entry *p1 = lp_to_fe(a, sorted), *p2 = lp_to_fe(b, sorted);
return (int)((long)p2->modified - (long)p1->modified);
}
struct lws_diskcache_scan *
lws_diskcache_create(const char *cache_dir_base, uint64_t cache_size_limit)
{
struct lws_diskcache_scan *lds = lws_malloc(sizeof(*lds), "cachescan");
if (!lds)
return NULL;
memset(lds, 0, sizeof(*lds));
lds->cache_dir_base = cache_dir_base;
lds->cache_size_limit = cache_size_limit;
return lds;
}
void
lws_diskcache_destroy(struct lws_diskcache_scan **lds)
{
if ((*lds)->batch)
lws_free((*lds)->batch);
lws_free(*lds);
*lds = NULL;
}
int
lws_diskcache_prepare(const char *cache_base_dir, int mode, uid_t uid)
{
char dir[256];
int n, m;
(void)mkdir(cache_base_dir, (unsigned short)mode);
if (chown(cache_base_dir, uid, (gid_t)-1))
lwsl_err("%s: %s: unable to chown %d\n", __func__,
cache_base_dir, uid);
for (n = 0; n < 16; n++) {
lws_snprintf(dir, sizeof(dir), "%s/%c", cache_base_dir, hex[n]);
(void)mkdir(dir, (mode_t)mode);
if (chown(dir, uid, (uid_t)-1))
lwsl_err("%s: %s: unable to chown %d\n", __func__,
dir, uid);
for (m = 0; m < 16; m++) {
lws_snprintf(dir, sizeof(dir), "%s/%c/%c",
cache_base_dir, hex[n], hex[m]);
(void)mkdir(dir, (mode_t)mode);
if (chown(dir, uid, (uid_t)-1))
lwsl_err("%s: %s: unable to chown %d\n",
__func__, dir, uid);
}
}
return 0;
}
/* copies and then truncates the incoming name, and renames the file at the
* untruncated path to have the new truncated name */
int
lws_diskcache_finalize_name(char *cache)
{
char ren[256], *p;
strncpy(ren, cache, sizeof(ren) - 1);
ren[sizeof(ren) - 1] = '\0';
p = strchr(cache, '~');
if (p) {
*p = '\0';
if (rename(ren, cache)) {
lwsl_err("%s: problem renaming %s to %s\n", __func__,
ren, cache);
return 1;
}
return 0;
}
return 1;
}
int
lws_diskcache_query(struct lws_diskcache_scan *lds, int is_bot,
const char *hash_hex, int *_fd, char *cache, int cache_len,
size_t *extant_cache_len)
{
struct stat s;
int n;
/* caching is disabled? */
if (!lds->cache_dir_base)
return LWS_DISKCACHE_QUERY_NO_CACHE;
if (!is_bot)
lds->cache_tries++;
n = lws_snprintf(cache, (size_t)cache_len, "%s/%c/%c/%s", lds->cache_dir_base,
hash_hex[0], hash_hex[1], hash_hex);
lwsl_info("%s: job cache %s\n", __func__, cache);
*_fd = open(cache, O_RDONLY);
if (*_fd >= 0) {
int fd;
if (!is_bot)
lds->cache_hits++;
if (fstat(*_fd, &s)) {
close(*_fd);
return LWS_DISKCACHE_QUERY_NO_CACHE;
}
*extant_cache_len = (size_t)s.st_size;
/* "touch" the hit cache file so it's last for LRU now */
fd = open(cache, O_RDWR);
if (fd >= 0)
close(fd);
return LWS_DISKCACHE_QUERY_EXISTS;
}
/* bots are too random to pollute the cache with their antics */
if (is_bot)
return LWS_DISKCACHE_QUERY_NO_CACHE;
/* let's create it first with a unique temp name */
lws_snprintf(cache + n, (size_t)cache_len - (unsigned int)n, "~%d-%p", (int)getpid(),
extant_cache_len);
*_fd = open(cache, O_RDWR | O_CREAT | O_TRUNC, 0600);
if (*_fd < 0) {
/* well... ok... we will proceed without cache then... */
lwsl_notice("%s: Problem creating cache %s: errno %d\n",
__func__, cache, errno);
return LWS_DISKCACHE_QUERY_NO_CACHE;
}
return LWS_DISKCACHE_QUERY_CREATING;
}
int
lws_diskcache_secs_to_idle(struct lws_diskcache_scan *lds)
{
return lds->secs_waiting;
}
/*
* The goal is to collect the oldest BATCH_COUNT filepaths and filesizes from
* the dirs under the cache dir. Since we don't need or want a full list of
* files in there in memory at once, we restrict the linked-list size to
* BATCH_COUNT entries, and once it is full, simply ignore any further files
* that are newer than the newest one on that list. Files older than the
* newest guy already on the list evict the newest guy already on the list
* and are sorted into the correct order. In this way no matter the number
* of files to be processed the memory requirement is fixed at BATCH_COUNT
* struct file_entry-s.
*
* The oldest subset of BATCH_COUNT files are sorted into the cd->batch
* allocation in more recent -> least recent order.
*
* We want to track the total size of all files we saw as well, so we know if
* we need to actually do anything yet to restrict how much space it's taking
* up.
*
* And we want to do those things statefully and incrementally instead of one
* big atomic operation, since the user may want a huge cache, so we look in
* one cache dir at a time and track state in the repodir struct.
*
* When we have seen everything, we add the doubly-linked prev pointers and then
* if we are over the limit, start deleting up to BATCH_COUNT files working back
* from the end.
*/
int
lws_diskcache_trim(struct lws_diskcache_scan *lds)
{
size_t cache_size_limit = (size_t)lds->cache_size_limit;
char dirpath[132], filepath[132 + 32];
lws_list_ptr lp, op = NULL;
int files_trimmed = 0;
struct file_entry *p;
int fd, n, ret = -1;
size_t trimmed = 0;
struct dirent *de;
struct stat s;
DIR *dir;
if (!lds->cache_subdir) {
if (lds->last_scan_completed + lds->secs_waiting > time(NULL))
return 0;
lds->batch = lws_malloc(sizeof(struct file_entry) *
BATCH_COUNT, "cache_trim");
if (!lds->batch) {
lwsl_err("%s: OOM\n", __func__);
return 1;
}
lds->agg_size = 0;
lds->head = NULL;
lds->batch_in_use = 0;
lds->agg_file_count = 0;
}
lws_snprintf(dirpath, sizeof(dirpath), "%s/%c/%c",
lds->cache_dir_base, hex[(lds->cache_subdir >> 4) & 15],
hex[lds->cache_subdir & 15]);
dir = opendir(dirpath);
if (!dir) {
lwsl_err("Unable to walk repo dir '%s'\n",
lds->cache_dir_base);
return -1;
}
do {
de = readdir(dir);
if (!de)
break;
if (de->d_type != DT_REG)
continue;
lds->agg_file_count++;
lws_snprintf(filepath, sizeof(filepath), "%s/%s", dirpath,
de->d_name);
fd = open(filepath, O_RDONLY);
if (fd < 0) {
lwsl_err("%s: cannot open %s\n", __func__, filepath);
continue;
}
n = fstat(fd, &s);
close(fd);
if (n) {
lwsl_notice("%s: cannot stat %s\n", __func__, filepath);
continue;
}
lds->agg_size += (uint64_t)s.st_size;
if (lds->batch_in_use == BATCH_COUNT) {
/*
* once we filled up the batch with candidates, we don't
* need to consider any files newer than the newest guy
* on the list...
*/
if (lp_to_fe(lds->head, sorted)->modified < s.st_mtime)
continue;
/*
* ... and if we find an older file later, we know it
* will be replacing the newest guy on the list, so use
* that directly...
*/
p = lds->head;
lds->head = p->sorted;
} else
/* we are still accepting anything to fill the batch */
p = &lds->batch[lds->batch_in_use++];
p->sorted = NULL;
strncpy(p->name, de->d_name, sizeof(p->name) - 1);
p->name[sizeof(p->name) - 1] = '\0';
p->modified = s.st_mtime;
p->size = (size_t)s.st_size;
lws_list_ptr_insert(&lds->head, &p->sorted, fe_modified_sort);
} while (de);
ret = 0;
lds->cache_subdir++;
if (lds->cache_subdir != 0x100)
goto done;
/* we completed the whole scan... */
/* if really no guidence, then 256MiB */
if (!cache_size_limit)
cache_size_limit = 256 * 1024 * 1024;
if (lds->agg_size > cache_size_limit) {
/* apply prev pointers to make the list doubly-linked */
lp = lds->head;
while (lp) {
p = lp_to_fe(lp, sorted);
p->prev = op;
op = &p->prev;
lp = p->sorted;
}
/*
* reverse the list (start from tail, now traverse using
* .prev)... it's oldest-first now...
*/
lp = op;
while (lp && lds->agg_size > cache_size_limit) {
p = lp_to_fe(lp, prev);
lws_snprintf(filepath, sizeof(filepath), "%s/%c/%c/%s",
lds->cache_dir_base, p->name[0],
p->name[1], p->name);
if (!unlink(filepath)) {
lds->agg_size -= p->size;
trimmed += p->size;
files_trimmed++;
} else
lwsl_notice("%s: Failed to unlink %s\n",
__func__, filepath);
lp = p->prev;
}
if (files_trimmed)
lwsl_notice("%s: %s: trimmed %d files totalling "
"%lldKib, leaving %lldMiB\n", __func__,
lds->cache_dir_base, files_trimmed,
((unsigned long long)trimmed) / KIB,
((unsigned long long)lds->agg_size) / MIB);
}
if (lds->agg_size && lds->agg_file_count)
lds->avg_size = lds->agg_size / (uint64_t)lds->agg_file_count;
/*
* estimate how long we can go before scanning again... default we need
* to start again immediately
*/
lds->last_scan_completed = time(NULL);
lds->secs_waiting = 1;
if (lds->agg_size < cache_size_limit) {
uint64_t avg = 4096, capacity, projected;
/* let's use 80% of the real average for margin */
if (lds->agg_size && lds->agg_file_count)
avg = ((lds->agg_size * 8) / (uint64_t)lds->agg_file_count) / 10;
/*
* if we collected BATCH_COUNT files of the average size,
* how much can we clean up in 256s?
*/
capacity = avg * BATCH_COUNT;
/*
* if the cache grew by 10%, would we hit the limit even then?
*/
projected = (lds->agg_size * 11) / 10;
if (projected < cache_size_limit)
/* no... */
lds->secs_waiting = (int)((256 / 2) * ((cache_size_limit -
projected) / capacity));
/*
* large waits imply we may not have enough info yet, so
* check once an hour at least.
*/
if (lds->secs_waiting > 3600)
lds->secs_waiting = 3600;
} else
lds->secs_waiting = 0;
lwsl_info("%s: cache %s: %lldKiB / %lldKiB, next scan %ds\n", __func__,
lds->cache_dir_base,
(unsigned long long)lds->agg_size / KIB,
(unsigned long long)cache_size_limit / KIB,
lds->secs_waiting);
lws_free(lds->batch);
lds->batch = NULL;
lds->cache_subdir = 0;
done:
closedir(dir);
return ret;
}

View File

@ -0,0 +1,528 @@
/*
* lws abstract display
*
* Copyright (C) 2013 Petteri Aimonen
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: mcufont font
*
* The mcu decoding is rewritten from the mcufont implementation at
* https://github.com/mcufont/mcufont, which is licensed under MIT already,
* to use a stateful decoder.
*
* The decoder only brings in new compression codes when needed to produce more
* pixels on the line of the glyphs being decoded.
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
#define DICT_START 24
#define REF_FILLZEROS 16
#define RLE_CODEMASK 0xC0
#define RLE_VALMASK 0x3F
#define RLE_ZEROS 0x00
#define RLE_64ZEROS 0x40
#define RLE_ONES 0x80
#define RLE_SHADE 0xC0
#define DICT_START7BIT 4
#define DICT_START6BIT 132
#define DICT_START5BIT 196
#define DICT_START4BIT 228
#define DICT_START3BIT 244
#define DICT_START2BIT 252
enum {
RS_IDLE,
RS_SKIP_PX,
RS_WRITE_PX,
RS_ALLZERO,
COMP = 0,
DICT1,
DICT1_CONT,
DICT2,
DICT3
};
typedef struct mcu_stack {
const uint8_t *dict;
int16_t dictlen;
int16_t runlen; /* for accumilation on DICT1 */
uint8_t byte;
uint8_t bitcount;
uint8_t state;
} mcu_stack_t;
typedef struct mcu_glyph {
lws_font_glyph_t fg;
const uint8_t *comp;
mcu_stack_t st[3];
int32_t runlen;
int8_t sp;
uint8_t runstate;
uint8_t alpha;
uint8_t code;
} mcu_glyph_t;
/* Get bit count for the "fill entries" */
static uint8_t
fillentry_bitcount(uint8_t index)
{
if (index >= DICT_START2BIT)
return 2;
else if (index >= DICT_START3BIT)
return 3;
else if (index >= DICT_START4BIT)
return 4;
else if (index >= DICT_START5BIT)
return 5;
else if (index >= DICT_START6BIT)
return 6;
else
return 7;
}
void
draw_px(lws_dlo_text_t *t, mcu_glyph_t *g)
{
lws_display_colour_t c = (lws_display_colour_t)((lws_display_colour_t)(g->alpha << 24) |
(lws_display_colour_t)((lws_display_colour_t)t->dlo.dc & 0xffffffu));
lws_fx_t t1, x;
int ex;
t1.whole = g->fg.x;
if (!g->alpha)
return;
t1.frac = 0;
lws_fx_add(&x, &g->fg.xpx, &t1);
#if 0
{ char b1[22], b2[22], b3[22];
lwsl_err("fadj %s = %s + %s\n",
lws_fx_string(&x, b1, sizeof(b1)),
lws_fx_string(&g->fg.xpx, b2, sizeof(b2)),
lws_fx_string(&g->fg.xorg, b3, sizeof(b3))); }
#endif
ex = x.whole;// - t->dlo.box.x.whole;
if (ex < 0 || ex >= t->dlo.box.w.whole) {
//lwsl_err("%s: ex %d (lim %d)\n", __func__, ex, t->dlo.box.w.whole);
return;
}
lws_fx_add(&x, &x, &g->fg.xorg);
lws_fx_add(&t1, &t->dlo.box.x, &x);
lws_surface_set_px(t->ic, t->line, t1.whole, &c);
}
static void
write_ref_codeword(mcu_glyph_t *g, const uint8_t *bf, uint8_t c)
{
uint32_t o, o1;
if (!c) {
g->runlen = 1;
g->runstate = RS_SKIP_PX;
return;
}
if (c <= 15) {
g->alpha = (uint8_t)(0x11 * c);
g->runlen = 1;
g->runstate = RS_WRITE_PX;
return;
}
if (c == REF_FILLZEROS) {
/* Fill with zeroes to end */
g->alpha = 0;
g->runlen = 1000000;
g->runstate = RS_WRITE_PX;
return;
}
if (c < DICT_START)
return;
if (c < DICT_START + lws_ser_ru32be(bf + MCUFO_COUNT_RLE_DICT)) {
/* write_rle_dictentry */
o1 = lws_ser_ru32be(bf + MCUFO_FOFS_DICT_OFS);
o = lws_ser_ru16be(bf + o1 + ((c - DICT_START) * 2));
g->st[(int)++g->sp].dictlen = (int16_t)(lws_ser_ru16be(bf + o1 +
((c - DICT_START + 1) * 2)) - o);
g->st[(int)g->sp].dict = bf + lws_ser_ru32be(bf + MCUFO_FOFS_DICT_DATA) + o;
g->st[(int)g->sp].state = DICT2;
return;
}
g->st[(int)++g->sp].bitcount = fillentry_bitcount(c);
g->st[(int)g->sp].byte = (uint8_t)(c - DICT_START7BIT);
g->st[(int)g->sp].state = DICT1;
g->runlen = 0;
}
static void
mcufont_next_code(mcu_glyph_t *g)
{
lws_dlo_text_t *t = lws_container_of(g->fg.list.owner, lws_dlo_text_t,
glyphs);
const uint8_t *bf = (const uint8_t *)t->font->data;
uint8_t c = *g->comp++;
uint32_t o, o1;
if (c < DICT_START + lws_ser_ru32be(&bf[MCUFO_COUNT_RLE_DICT]) ||
c >= DICT_START + lws_ser_ru32be(&bf[MCUFO_COUNT_REF_RLE_DICT])) {
write_ref_codeword(g, bf, c);
return;
}
/* write_ref_dictentry() */
o1 = lws_ser_ru32be(bf + MCUFO_FOFS_DICT_OFS);
o = lws_ser_ru16be(bf + o1 + ((c - DICT_START) * 2));
g->st[(int)++g->sp].dictlen = (int16_t)(lws_ser_ru16be(bf + o1 +
((c - DICT_START + 1) * 2)) - o);
g->st[(int)g->sp].dict = bf + lws_ser_ru32be(bf + MCUFO_FOFS_DICT_DATA) + o;
g->st[(int)g->sp].state = DICT3;
}
/* lookup and append a glyph for specific unicode to the text glyph list */
static uint32_t
font_mcufont_uniglyph_lookup(lws_dlo_text_t *text, uint32_t unicode)
{
const uint8_t *bf = (const uint8_t *)text->font->data,
*r = bf + lws_ser_ru32be(&bf[MCUFO_FOFS_CHAR_RANGE_TABLES]);
uint32_t entries = lws_ser_ru32be(&bf[MCUFO_COUNT_CHAR_RANGE_TABLES]);
unsigned int n;
if (entries > 8) /* coverity sanity */
return 0;
do {
for (n = 0; n < entries; n++) {
uint32_t cs = lws_ser_ru32be(r + 0), ce = lws_ser_ru32be(r + 4);
if (cs >= 0x100000 || !ce || ce > 0x10000)
return 0;
if (unicode >= cs && unicode < cs + ce) {
uint32_t cbo = lws_ser_ru32be(r + 0xc);
if (cbo >= text->font->data_len)
return 0;
cbo += lws_ser_ru16be(bf +
lws_ser_ru32be(r + 8) + ((unicode - cs) * 2));
if (cbo >= text->font->data_len)
return 0;
return cbo;
}
r += 16;
}
if (unicode == lws_ser_ru32be(&bf[MCUFO_UNICODE_FALLBACK]))
return 0;
unicode = lws_ser_ru32be(&bf[MCUFO_UNICODE_FALLBACK]);
} while (1);
}
static mcu_glyph_t *
font_mcufont_uniglyph(lws_dlo_text_t *text, uint32_t unicode)
{
const uint8_t *bf = (const uint8_t *)text->font->data;
uint32_t ofs;
mcu_glyph_t *g;
ofs = font_mcufont_uniglyph_lookup(text, unicode);
if (!ofs)
return NULL;
// lwsl_warn("%s: text->text_len %u: %c\n", __func__, text->text_len, (char)unicode);
g = lwsac_use_zero(&text->ac_glyphs, sizeof(*g),
(text->text_len + 1) * sizeof(*g));
if (!g)
return NULL;
g->comp = bf + ofs;
g->fg.cwidth.whole = *g->comp++;
g->fg.cwidth.frac = 0;
lws_dll2_add_tail(&g->fg.list, &text->glyphs);
return g;
}
int
lws_display_font_mcufont_getcwidth(lws_dlo_text_t *text, uint32_t unicode,
lws_fx_t *fx)
{
const uint8_t *bf = (const uint8_t *)text->font->data;
uint32_t ofs = font_mcufont_uniglyph_lookup(text, unicode);
if (!ofs)
return 1;
fx->whole = bf[ofs];
fx->frac = 0;
return 0;
}
lws_font_glyph_t *
lws_display_font_mcufont_image_glyph(lws_dlo_text_t *text, uint32_t unicode,
char attach)
{
const uint8_t *bf = (const uint8_t *)text->font->data;
mcu_glyph_t *g;
/* one text dlo has glyphs from all the same fonts and attributes */
if (!text->font_height) {
text->font_height = (int16_t)lws_ser_ru16be(&bf[MCUFO16_HEIGHT]);
text->font_y_baseline = (int16_t)(text->font_height -
lws_ser_ru16be(&bf[MCUFO16_BASELINE_Y]));
text->font_line_height = (int16_t)lws_ser_ru16be(&bf[MCUFO16_LINE_HEIGHT]);
}
lws_display_font_mcufont_getcwidth(text, unicode, &text->_cwidth);
if (!attach)
return NULL;
g = font_mcufont_uniglyph(text, unicode);
if (!g)
return NULL;
g->fg.height.whole = lws_ser_ru16be(bf + MCUFO16_HEIGHT);
g->fg.height.frac = 0;
return &g->fg;
}
lws_stateful_ret_t
lws_display_font_mcufont_render(struct lws_display_render_state *rs)
{
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo);
const uint8_t *bf = (const uint8_t *)text->font->data;
lws_fx_t ax, ay, t, t1, t2, t3;
mcu_glyph_t *g;
int s, e, yo;
uint8_t c, el;
lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&t, &ax, &dlo->box.w);
lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y);
lws_fx_add(&t1, &ay, &dlo->box.h);
lws_fx_add(&t2, &ax, &text->bounding_box.w);
text->curr = rs->curr;
text->ic = rs->ic;
text->line = rs->line;
s = ax.whole;
e = lws_fx_roundup(&t2);
if (e <= 0)
return LWS_SRET_OK; /* wholly off to the left */
if (s >= rs->ic->wh_px[0].whole)
return LWS_SRET_OK; /* wholly off to the right */
if (e >= rs->ic->wh_px[0].whole)
e = rs->ic->wh_px[0].whole;
/* figure out our y position inside the glyph bounding box */
yo = rs->curr - ay.whole;
if (!yo) {
lws_display_dlo_text_attach_glyphs(text);
t3.whole = lws_ser_ru16be(bf + MCUFO16_BASELINE_X);
t3.frac = 0;
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&text->glyphs)) {
lws_font_glyph_t *fg = lws_container_of(d, lws_font_glyph_t, list);
lws_fx_sub(&fg->xpx, &fg->xpx, &t3);
fg->xorg = rs->st[rs->sp].co.x;
} lws_end_foreach_dll(d);
}
#if 0
{
uint32_t dc = 0xff0000ff;
int s1 = s;
/* from origin.x + dlo->box.x */
for (s1 = ax.whole; s1 < t2.whole; s1++)
lws_surface_set_px(ic, line, s1, &dc);
memset(&ce, 0, sizeof(ce));
}
#endif
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&text->glyphs)) {
lws_font_glyph_t *fg = lws_container_of(d, lws_font_glyph_t, list);
g = (mcu_glyph_t *)fg;
fg->x = 0;
while (yo < (int)fg->height.whole &&
fg->x < lws_ser_ru16be(bf + MCUFO16_WIDTH)) {
switch (g->runstate) {
case RS_IDLE:
switch (g->st[(int)g->sp].state) {
case COMP:
mcufont_next_code(g);
break;
case DICT1_CONT:
--g->sp; /* back to DICT1 after doing the skip */
g->runstate = RS_SKIP_PX;
g->runlen = 1;
continue;
case DICT1:
/* write_bin_codeword() states */
el = 0;
while (g->st[(int)g->sp].bitcount--) {
c = g->st[(int)g->sp].byte;
g->st[(int)g->sp].byte >>= 1;
if (c & 1)
g->st[(int)g->sp].runlen++;
else {
if (g->st[(int)g->sp].runlen) {
g->alpha = 255;
g->runstate = RS_WRITE_PX;
g->runlen = g->st[(int)g->sp].runlen;
g->st[(int)g->sp].runlen = 0;
g->st[(int)++g->sp].state = DICT1_CONT;
el = 1;
break;
}
g->runstate = RS_SKIP_PX;
g->runlen = 1;
el = 1;
break;
}
}
if (el)
continue;
/* back out of DICT1 */
if (!g->sp)
assert(0);
g->sp--;
if (g->st[(int)g->sp + 1].runlen) {
g->alpha = 255;
g->runstate = RS_WRITE_PX;
g->runlen = g->st[(int)g->sp + 1].runlen;
g->st[(int)g->sp + 1].runlen = 0;
continue;
}
break;
case DICT2: /* write_rle_dictentry */
c = (*g->st[(int)g->sp].dict++);
if (!--g->st[(int)g->sp].dictlen) {
if (!g->sp)
assert(0);
g->sp--;
}
if ((c & RLE_CODEMASK) == RLE_ZEROS) {
g->runstate = RS_SKIP_PX;
g->runlen = c & RLE_VALMASK;
continue;
}
if ((c & RLE_CODEMASK) == RLE_64ZEROS) {
g->runstate = RS_SKIP_PX;
g->runlen = ((c & RLE_VALMASK) + 1) * 64;
continue;
}
if ((c & RLE_CODEMASK) == RLE_ONES) {
g->alpha = 255;
g->runstate = RS_WRITE_PX;
g->runlen = (c & RLE_VALMASK) + 1;
continue;
}
if ((c & RLE_CODEMASK) == RLE_SHADE) {
g->alpha = (uint8_t)(((c & RLE_VALMASK) & 0xf) * 0x11);
g->runstate = RS_WRITE_PX;
g->runlen = ((c & RLE_VALMASK) >> 4) + 1;
continue;
}
break;
case DICT3:
c = *g->st[(int)g->sp].dict++;
if (!--g->st[(int)g->sp].dictlen) {
if (!g->sp)
assert(0);
g->sp--;
}
write_ref_codeword(g, bf, c);
break;
}
break;
case RS_SKIP_PX:
fg->x++;
if (--g->runlen)
break;
g->runstate = RS_IDLE;
break;
case RS_WRITE_PX:
if (g->alpha)
draw_px(text, g);
g->fg.x++;
if (--g->runlen)
break;
g->runstate = RS_IDLE;
break;
case RS_ALLZERO:
fg->x++;
if (--g->runlen)
break;
g->runstate = RS_IDLE;
break;
}
}
} lws_end_foreach_dll(d);
return LWS_SRET_OK;
}

View File

@ -0,0 +1,195 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: JPEG
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
void
lws_display_dlo_jpeg_destroy(struct lws_dlo *dlo)
{
lws_dlo_jpeg_t *dlo_jpeg = lws_container_of(dlo, lws_dlo_jpeg_t, dlo);
#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_SECURE_STREAMS)
lws_ss_destroy(&dlo_jpeg->flow.h);
#endif
lws_buflist_destroy_all_segments(&dlo_jpeg->flow.bl);
if (dlo_jpeg->j)
lws_jpeg_free(&dlo_jpeg->j);
}
lws_stateful_ret_t
lws_display_render_jpeg(struct lws_display_render_state *rs)
{
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_dlo_jpeg_t *dlo_jpeg = lws_container_of(dlo, lws_dlo_jpeg_t, dlo);
lws_display_colour_t pc;
lws_fx_t ax, ay, t, t1;
lws_stateful_ret_t r;
const uint8_t *pix;
int s, e;
lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&t, &ax, &dlo->box.w);
lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y);
lws_fx_add(&t1, &ay, &dlo->box.h);
if (!lws_jpeg_get_height(dlo_jpeg->j)) {
lwsl_info("%s: jpeg does not have dimensions yet\n", __func__);
return LWS_SRET_WANT_INPUT;
}
s = ax.whole;
e = lws_fx_roundup(&t);
if (rs->curr > lws_fx_roundup(&t1))
return LWS_SRET_OK;
if (rs->curr - lws_fx_roundup(&ay) >
(int)lws_jpeg_get_height(dlo_jpeg->j))
return LWS_SRET_OK;
if (s < 0)
s = 0;
if (s > rs->ic->wh_px[0].whole)
return LWS_SRET_OK; /* off to the right */
if (e > rs->ic->wh_px[0].whole)
e = rs->ic->wh_px[0].whole - 1;
if (e <= 0)
return LWS_SRET_OK; /* off to the left */
do {
if (lws_flow_feed(&dlo_jpeg->flow))
/* if he says WANT_INPUT, we have nothing in the buflist */
return LWS_SRET_WANT_INPUT;
pix = NULL;
r = lws_jpeg_emit_next_line(dlo_jpeg->j, &pix, &dlo_jpeg->flow.data,
&dlo_jpeg->flow.len, rs->html == 1);
if (r & LWS_SRET_NO_FURTHER_IN)
dlo_jpeg->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED;
if (r & LWS_SRET_FATAL || r == LWS_SRET_OK)
return r;
r = lws_flow_req(&dlo_jpeg->flow);
if (r & LWS_SRET_WANT_INPUT)
return r;
} while (!pix);
/*
* What's in pix is either 24-bit RGB 3 bytes/px, or 8-bit grayscale
* 1 byte/px, we have to map it on to either 32-bit RGBA or 16-bit YA
* composition buf
*/
pix = pix + (( (unsigned int)(s - ax.whole) *
(lws_jpeg_get_pixelsize(dlo_jpeg->j) / 8)));
while (s < e && s >= ax.whole && s < lws_fx_roundup(&t) &&
(s - ax.whole) < (int)lws_jpeg_get_width(dlo_jpeg->j)) {
if (lws_jpeg_get_pixelsize(dlo_jpeg->j) == 8)
pc = LWSDC_RGBA(pix[0], pix[0], pix[0], 255);
else
pc = LWSDC_RGBA(pix[0], pix[1], pix[2], 255);
lws_surface_set_px(rs->ic, rs->line, s, &pc);
s++;
pix += lws_jpeg_get_pixelsize(dlo_jpeg->j) / 8;
}
return LWS_SRET_OK;
}
lws_stateful_ret_t
lws_display_dlo_jpeg_metadata_scan(lws_dlo_jpeg_t *dlo_jpeg)
{
lws_stateful_ret_t r;
size_t l, l1;
const uint8_t *pix;
/*
* If we don't have the image metadata yet, provide small chunks of the
* source data until we do have the image metadata, but small enough
* we can't produce any decoded pixels too early.
*/
while (!lws_jpeg_get_height(dlo_jpeg->j) && dlo_jpeg->flow.len) {
l1 = l = dlo_jpeg->flow.len > 128 ? 128 : dlo_jpeg->flow.len;
r = lws_jpeg_emit_next_line(dlo_jpeg->j, &pix, &dlo_jpeg->flow.data, &l, 1);
if (r >= LWS_SRET_FATAL) {
lwsl_err("%s: hdr parse failed %d\n", __func__, r);
return r;
}
dlo_jpeg->flow.len -= l1 - l;
if (lws_jpeg_get_height(dlo_jpeg->j)) {
lwsl_info("jpeg: w %d, h %d\n",
lws_jpeg_get_width(dlo_jpeg->j),
lws_jpeg_get_height(dlo_jpeg->j));
return LWS_SRET_OK;
}
}
return LWS_SRET_WANT_INPUT;
}
lws_dlo_jpeg_t *
lws_display_dlo_jpeg_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box)
{
lws_dlo_jpeg_t *dlo_jpeg = lws_zalloc(sizeof(*dlo_jpeg), __func__);
if (!dlo_jpeg)
return NULL;
dlo_jpeg->j = lws_jpeg_new();
if (!dlo_jpeg->j)
goto bail;
dlo_jpeg->dlo.box = *box;
dlo_jpeg->dlo.render = lws_display_render_jpeg;
dlo_jpeg->dlo._destroy = lws_display_dlo_jpeg_destroy;
lws_display_dlo_add(dl, dlo_parent, &dlo_jpeg->dlo);
return dlo_jpeg;
bail:
lwsl_err("%s: bailed\n", __func__);
if (dlo_jpeg->j)
lws_jpeg_free(&dlo_jpeg->j);
lws_free(dlo_jpeg);
return NULL;
}

View File

@ -0,0 +1,982 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List LHP layout
*
* The basic flow is logical elements exist in a stack as they are parsed, the
* job of lhp_displaylist_layout() is to translate these into a tree of DLOs,
* having parent-child relationships with (x,y) of the DLO box being an offset
* into a local origin formed from the DLO parent box (which in turn may be
* a child with its origin defined by its parent, etc).
*
* The element stack only exists while it and its parent elements are being
* parsed, it goes out of scope as the element ends. So we must create related
* DLOs by stream-parsing, while we still have everything relevant to hand.
*
* This gets us out of having to run around fixing up DLO (x,y) as we do the
* layout, since the DLO parent-child relationships are static even if their
* content size isn't.
*
*
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
/*
* HTML Elements we can deal with for layout
*/
enum {
/* 0 is no match */
LHP_ELEM_BR = 1,
LHP_ELEM_DIV,
LHP_ELEM_TABLE,
LHP_ELEM_TR,
LHP_ELEM_TD,
LHP_ELEM_IMG,
};
static const struct {
const char *elem;
uint8_t elem_len;
} elems[] = {
{ "br", 2 },
{ "div", 3 },
{ "table", 5 },
{ "tr", 2 },
{ "td", 2 },
{ "img", 3 },
};
/*
* Newline moves the psb->cury to cover text that was already placed using the
* old psb->cury as to top of it. So a final newline on the last line of text
* does not create an extra blank line.
*/
static const lws_fx_t two = { 2,0 };
static void
newline(lhp_ctx_t *ctx, lhp_pstack_t *psb, lhp_pstack_t *ps,
lws_displaylist_t *dl)
{
int16_t group_baseline = 9999, group_height = 0;
lws_fx_t line_height = { 0, 0 }, w, add, ew, t1;
const struct lcsp_atr *a;
lws_dlo_t *dlo, *d, *d1;
int t = 0;
if (!psb || !ps) {
lwsl_err("%s: psb/ps NULL!\n", __func__);
return;
}
dlo = (lws_dlo_t *)psb->dlo;
lws_fx_add(&w, lws_csp_px(ps->css_padding[CCPAS_LEFT], ps),
lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps));
if (lws_fx_comp(&w, &psb->widest) > 0)
psb->widest = w;
if (!dlo || !dlo->children.tail)
return;
d = lws_container_of(dlo->children.tail, lws_dlo_t, list);
/*
* We may be at the end of a line of text
*
* Figure out the biggest height on the line, and the total width
*/
while (d) {
t |= d->_destroy == lws_display_dlo_text_destroy;
/* find the "worst" height on the line */
if (lws_fx_comp(&d->box.h, &line_height) > 0)
line_height = d->box.h;
if (d->_destroy == lws_display_dlo_text_destroy) {
lws_dlo_text_t *text = lws_container_of(d,
lws_dlo_text_t, dlo.list);
if (text->font_y_baseline < group_baseline)
group_baseline = text->font_y_baseline;
if (text->font_height > group_height)
group_height = text->font_height;
}
if (!d->flag_runon)
break;
d = lws_container_of(d->list.prev, lws_dlo_t, list);
};
/* mark the related text dlos with information about group bl and h,
* offset box y to align to group baseline if necessary */
d1 = d;
while (d) {
if (d->_destroy == lws_display_dlo_text_destroy) {
lws_dlo_text_t *t1 = lws_container_of(d1,
lws_dlo_text_t, dlo.list);
lws_fx_t ft;
t1->group_height = group_height;
t1->group_y_baseline = group_baseline;
ft.whole = (t1->font_height - t1->font_y_baseline) -
(group_height - group_baseline);
ft.frac = 0;
lws_fx_sub(&t1->dlo.box.y, &t1->dlo.box.y, &ft);
}
if (!d1->list.next)
break;
d1 = lws_container_of(d1->list.next, lws_dlo_t, list);
};
w = psb->curx;
ew = ctx->ic.wh_px[0];
if (psb->css_width && psb->css_width->unit != LCSP_UNIT_NONE)
ew = *lws_csp_px(psb->css_width, psb);
lws_fx_sub(&ew, &ew, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
lws_fx_sub(&ew, &ew, lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps));
if (lws_fx_comp(&w, &psb->widest) > 0)
psb->widest = w;
if (!t) /* no textual children to newline (eg, <div></div>) */
return;
/*
* now is our chance to fix up dlos that are part of the line for
* text-align rule of the container.
*/
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TEXT_ALIGN);
if (a) {
switch (a->propval) {
case LCSP_PROPVAL_CENTER:
add = *lws_csp_px(ps->css_padding[CCPAS_LEFT], ps);
lws_fx_sub(&t1, &ew, &w);
lws_fx_div(&t1, &t1, &two);
lws_fx_add(&add, &add, &t1);
goto fixup;
case LCSP_PROPVAL_RIGHT:
lws_fx_sub(&add, &ew, &w);
lws_fx_sub(&add, &add, &d->box.x);
fixup:
lws_fx_add(&t1, &add, &w);
if (lws_fx_comp(&t1, &psb->widest) > 0)
psb->widest = t1;
do {
lws_fx_add(&d->box.x, &d->box.x, &add);
if (!d->list.next)
break;
d = lws_container_of(d->list.next, lws_dlo_t,
list);
} while (1);
break;
default:
break;
}
}
lws_fx_add(&psb->cury, &psb->cury, &line_height);
lws_fx_set(psb->curx, 0, 0);
psb->dlo_set_curx = NULL;
psb->dlo_set_cury = NULL;
psb->runon = 0;
}
void
lhp_set_dlo_padding_margin(lhp_pstack_t *ps, lws_dlo_t *dlo)
{
int n;
for (n = 0; n < 4; n ++) {
if (ps->css_margin[n])
dlo->margin[n] = *lws_csp_px(ps->css_margin[n], ps);
else
lws_fx_set(dlo->margin[n], 0, 0);
if (ps->css_padding[n])
dlo->padding[n] = *lws_csp_px(ps->css_padding[n], ps);
else
lws_fx_set(dlo->padding[n], 0, 0);
}
}
void
lhp_set_dlo_adjust_to_contents(lhp_pstack_t *ps)
{
lhp_pstack_t *psb = lws_container_of(ps->list.prev, lhp_pstack_t, list);
lws_dlo_dim_t dim;
lws_dlo_contents(ps->dlo, &dim);
lws_display_dlo_adjust_dims(ps->dlo, &dim);
if (lws_fx_comp(&dim.w, &psb->widest) > 0)
psb->widest = dim.w;
if (lws_fx_comp(&dim.h, &psb->deepest) > 0)
psb->deepest = dim.h;
}
static void
runon(lhp_pstack_t *ps, lws_dlo_t *dlo)
{
dlo->flag_runon = (uint8_t)(ps->runon & 1);
ps->runon = 1;
}
/*
* Handle end-of-div, table, tr, td retrospective dlo dimension adjustment
*/
int
lws_lhp_dlo_adjust_div_type_element(lhp_ctx_t *ctx, lhp_pstack_t *psb,
lhp_pstack_t *pst, lhp_pstack_t *ps,
int elem_match)
{
lws_dlo_rect_t *rect = (lws_dlo_rect_t *)ps->dlo;
lws_fx_t t1, w, wd;
char rd = 0;
/* need this to get bottom clearance for next block */
lws_fx_add(&ps->cury, &ps->cury,
lws_csp_px(ps->css_padding[CCPAS_BOTTOM], ps));
if (psb && ps->dlo &&
ps->css_margin[CCPAS_LEFT]->propval == LCSP_PROPVAL_AUTO &&
ps->css_margin[CCPAS_RIGHT]->propval == LCSP_PROPVAL_AUTO) {
lws_dlo_rect_t *re = (lws_dlo_rect_t *)ps->dlo;
/* h-center a div... find the available h space first */
w = ctx->ic.wh_px[LWS_LHPREF_WIDTH];
if (psb->css_width &&
psb->css_width->propval != LCSP_PROPVAL_AUTO)
w = *lws_csp_px(psb->css_width, psb);
lws_fx_sub(&t1, &w, &re->dlo.box.w);
lws_fx_div(&t1, &t1, &two);
lws_fx_sub(&wd, &t1, &re->dlo.box.x);
lws_fx_add(&re->dlo.box.x, &re->dlo.box.x, &wd);
}
/* fix up the dimensions of div rectangle */
if (!rect) {
lwsl_notice("%s: elem %d: NO RECT\n", __func__, elem_match);
return 1;
}
lhp_set_dlo_adjust_to_contents(ps);
/* if a td, deal with columnar changes in width */
if (ps->dlo->col_list.owner) {
lhp_table_col_t *tc = lws_container_of(
ps->dlo->col_list.owner,
lhp_table_col_t, col_dlos);
lws_fx_t wdelta, ow;
ow = tc->width;
lws_fx_set(tc->width, 0, 0);
/* discover the new width of column */
lws_start_foreach_dll(struct lws_dll2 *, c1,
lws_dll2_get_head(&tc->col_dlos)) {
lws_dlo_t *dloc = lws_container_of(c1,
lws_dlo_t, col_list);
if (lws_fx_comp(&dloc->box.w, &tc->width) > 0)
tc->width = dloc->box.w;
} lws_end_foreach_dll(c1);
/* new width - old column width */
lws_fx_sub(&wdelta, &tc->width, &ow);
/*
* Update all dlos in our column (except
* ourselves) with the increased column width
*/
lws_start_foreach_dll(struct lws_dll2 *, cold,
lws_dll2_get_head(&tc->col_dlos)) {
lws_dlo_t *dloc = lws_container_of(cold,
lws_dlo_t, col_list);
if (dloc != &rect->dlo)
/* we already did this for the
* affected dlo */
lws_fx_add(&dloc->box.w,
&dloc->box.w, &wdelta);
rd = 1;
/* ... and then all of their row-mates
* to the right also need their
* x adjusting then */
while (dloc->row_list.next) {
dloc = lws_container_of(
dloc->row_list.next,
lws_dlo_t, row_list);
lws_fx_add(&dloc->box.x,
&dloc->box.x, &wdelta);
}
} lws_end_foreach_dll(cold);
}
/* if a td, deal with row changes in height */
if (ps->dlo->row_list.owner) {
lhp_table_row_t *tr = lws_container_of(
ps->dlo->row_list.owner,
lhp_table_row_t, row_dlos);
lws_fx_t hdelta, oh;
oh = tr->height;
lws_fx_set(tr->height, 0, 0);
/* discover the new width of column */
lws_start_foreach_dll(struct lws_dll2 *, r1,
lws_dll2_get_head(&tr->row_dlos)) {
lws_dlo_t *dlor = lws_container_of(r1,
lws_dlo_t, row_list);
if (lws_fx_comp(&dlor->box.h, &tr->height) > 0)
tr->height = dlor->box.h;
} lws_end_foreach_dll(r1);
/* new height - old row height */
lws_fx_sub(&hdelta, &tr->height, &oh);
/*
* Update all dlos in our row (except
* ourselves) with the increased row height
*/
lws_start_foreach_dll(struct lws_dll2 *, rold,
lws_dll2_get_head(&tr->row_dlos)) {
lws_dlo_t *dlor = lws_container_of(rold,
lws_dlo_t, row_list);
if (dlor != &rect->dlo)
/* we already did this for the
* affected dlo */
lws_fx_add(&dlor->box.h,
&dlor->box.h, &hdelta);
/* ... so all of their col-mates below
* also need their y adjusting then */
while (dlor->col_list.next) {
dlor = lws_container_of(
dlor->col_list.next,
lws_dlo_t, col_list);
lws_fx_add(&dlor->box.y,
&dlor->box.y, &hdelta);
}
rd = 1;
} lws_end_foreach_dll(rold);
}
/*
* Row dimensions have to be reassessed?
*/
if (rd) {
lws_start_foreach_dll(struct lws_dll2 *, ro,
lws_dll2_get_head(&pst->dlo->children)) {
lws_dlo_t *dlo = lws_container_of(ro, lws_dlo_t, list);
lws_dlo_dim_t dim;
lws_dlo_contents(dlo, &dim);
lws_display_dlo_adjust_dims(dlo, &dim);
} lws_end_foreach_dll(ro);
}
if (psb && ps->css_position->propval != LCSP_PROPVAL_ABSOLUTE) {
/* parent should account for our margin */
if (elem_match == LHP_ELEM_DIV) {
lws_fx_add(&psb->curx, &psb->curx, &ps->widest);
/* now we applied ps->widest, reset it */
lws_fx_set(ps->widest, 0, 0);
psb->dlo_set_curx = ps->dlo;
} else {
/* needed for margin between table cells */
lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_LEFT], ps));
lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
}
if (elem_match != LHP_ELEM_TD) {
if (ps->css_display->propval != LCSP_PROPVAL_INLINE_BLOCK) {
lws_fx_add(&psb->cury, &psb->cury, &ps->dlo->box.h);
psb->dlo_set_cury = ps->dlo;
}
// lws_fx_add(&psb->cury, &psb->cury, &ps->dlo->margin[CCPAS_BOTTOM]);
} else
ps->widest = ps->dlo->box.w;
}
return 0;
}
/*
* Generic LHP displaylist object layout callback... converts html elements
* into DLOs on the display list
*/
lws_stateful_ret_t
lhp_displaylist_layout(lhp_ctx_t *ctx, char reason)
{
lhp_pstack_t *psb = NULL, *pst = NULL, *psp = NULL,
*ps = lws_container_of(ctx->stack.tail, lhp_pstack_t, list);
struct lws_context *cx = (struct lws_context *)ctx->user1;
lws_dl_rend_t *drt = (lws_dl_rend_t *)ctx->user;
lws_fx_t br[4], t1, indent, ox, w, h;
const lws_display_font_t *f = NULL;
lhp_table_col_t *tcol = NULL;
lhp_table_row_t *trow = NULL;
lws_dlo_t *abut_x, *abut_y;
uint32_t col = 0xff000000;
lws_dlo_text_t *txt;
const lcsp_atr_t *a;
lws_dlo_image_t u;
const char *pname;
char lastm = 0;
int elem_match;
lws_box_t box;
char url[128];
int n, s = 0;
/* default font choice */
lws_font_choice_t fc = {
.family_name = "term, serif",
.fixed_height = 16,
.weight = 400,
};
if (!ps->font) {
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_SIZE);
if (a)
fc.fixed_height = (uint16_t)a->u.i.whole;
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_FAMILY);
if (a)
fc.family_name = (const char *)&a[1];
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_WEIGHT);
if (a) {
switch (a->propval) {
case LCSP_PROPVAL_BOLD:
fc.weight = 700;
break;
case LCSP_PROPVAL_BOLDER:
fc.weight = 800;
break;
default:
if (a->u.i.whole)
fc.weight = (uint16_t)a->u.i.whole;
break;
}
}
ps->font = lws_font_choose(cx, &fc);
}
f = ps->font;
psb = lws_css_get_parent_block(ctx, ps);
elem_match = 0;
for (n = 0; n < (int)LWS_ARRAY_SIZE(elems); n++)
if (ctx->npos == elems[n].elem_len &&
!memcmp(ctx->buf, elems[n].elem, elems[n].elem_len))
elem_match = n + 1;
switch (reason) {
case LHPCB_CONSTRUCTED:
case LHPCB_DESTRUCTED:
case LHPCB_COMPLETE:
case LHPCB_FAILED:
break;
case LHPCB_ELEMENT_START:
switch (elem_match) {
case LHP_ELEM_BR:
newline(ctx, psb, ps, drt->dl);
break;
case LHP_ELEM_TR:
if (!psb)
break;
pst = ps;
while (pst && !pst->is_table)
pst = lws_css_get_parent_block(ctx, pst);
if (!pst) {
lwsl_err("%s: td: no table found\n", __func__);
break;
}
pst->curx.whole = 0;
pst->curx.frac = 0;
psb->dlo_set_curx = NULL;
trow = lws_zalloc(sizeof(*trow), __func__);
if (!trow) {
lwsl_err("%s: OOM\n", __func__);
return LWS_SRET_FATAL;
}
lws_dll2_add_tail(&trow->list, &pst->dlo->table_rows);
trow = NULL;
pst->td_idx = 0;
goto do_rect;
case LHP_ELEM_TD:
if (!psb) {
lwsl_err("%s: td: no psb found\n", __func__);
break;
}
pst = ps;
while (pst && !pst->is_table)
pst = lws_css_get_parent_block(ctx, pst);
if (!pst) {
lwsl_err("%s: td: no table found\n", __func__);
break;
}
if (pst->td_idx >= (int)pst->dlo->table_cols.count) {
tcol = lws_zalloc(sizeof(*tcol), __func__);
if (!tcol) {
lwsl_err("%s: OOM\n", __func__);
return LWS_SRET_FATAL;
}
lws_dll2_add_tail(&tcol->list, &pst->dlo->table_cols);
} else {
tcol = lws_container_of(pst->dlo->table_cols.head, lhp_table_col_t, list);
n = pst->td_idx;
while (n--)
tcol = lws_container_of(tcol->list.next, lhp_table_col_t, list);
}
if (pst->dlo->table_rows.tail)
trow = lws_container_of(pst->dlo->table_rows.tail, lhp_table_row_t, list);
goto do_rect;
case LHP_ELEM_TABLE:
ps->is_table = 1;
/* fallthru */
case LHP_ELEM_DIV:
do_rect:
lws_fx_set(box.x, 0, 0);
lws_fx_set(box.y, 0, 0);
lws_fx_set(box.h, 0, 0);
lws_fx_set(box.w, 0, 0);
abut_x = NULL;
abut_y = NULL;
if (ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE) {
box.x = *lws_csp_px(ps->css_pos[CCPAS_LEFT], ps);
box.y = *lws_csp_px(ps->css_pos[CCPAS_TOP], ps);
} else {
if (psb) {
/* margin adjusts our child box origin */
lws_fx_add(&box.x, &psb->curx,
lws_csp_px(ps->css_margin[CCPAS_LEFT], ps));
box.y = psb->cury;
abut_x = psb->dlo_set_curx;
abut_y = psb->dlo_set_cury;
//lws_fx_add(&box.y, &psb->cury,
// lws_csp_px(ps->css_margin[CCPAS_TOP], ps));
}
}
/* If there's an explicit width, try to go with that */
if (ps->css_width &&
ps->css_width->unit != LCSP_UNIT_NONE &&
lws_fx_comp(lws_csp_px(ps->css_width, ps), &box.w) < 0)
box.w = *lws_csp_px(ps->css_width, ps);
/* !!! we rely on this being nonzero to not infinite loop at text layout */
lws_fx_add(&box.w, &box.w,
lws_csp_px(ps->css_padding[CCPAS_LEFT], ps));
lws_fx_add(&box.w, &box.w,
lws_csp_px(ps->css_padding[CCPAS_RIGHT], ps));
ps->drt.w = box.w;
ps->curx = *lws_csp_px(ps->css_padding[CCPAS_LEFT], ps);
ps->cury = *lws_csp_px(ps->css_padding[CCPAS_TOP], ps);
memset(br, 0, sizeof(br));
if (ps->css_border_radius[0])
br[0] = *lws_csp_px(ps->css_border_radius[0], ps);
if (ps->css_border_radius[1])
br[1] = *lws_csp_px(ps->css_border_radius[1], ps);
if (ps->css_border_radius[2])
br[2] = *lws_csp_px(ps->css_border_radius[2], ps);
if (ps->css_border_radius[3])
br[3] = *lws_csp_px(ps->css_border_radius[3], ps);
psp = lws_container_of(ps->list.prev, lhp_pstack_t, list);
ps->dlo = (lws_dlo_t *)lws_display_dlo_rect_new(drt->dl,
ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE ? NULL : psp->dlo,
&box, br, ps->css_background_color ?
ps->css_background_color->u.rgba : 0);
if (!ps->dlo) {
lwsl_err("%s: FAILED to create rect\n", __func__);
return LWS_SRET_FATAL;
}
ps->dlo->abut_x = abut_x;
ps->dlo->abut_y = abut_y;
if (psb)
lws_fx_add(&psb->curx, &psb->curx,
lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
if (tcol)
lws_dll2_add_tail(&ps->dlo->col_list, &tcol->col_dlos);
if (trow)
lws_dll2_add_tail(&ps->dlo->row_list, &trow->row_dlos);
lws_lhp_tag_dlo_id(ctx, ps, ps->dlo);
lhp_set_dlo_padding_margin(ps, ps->dlo);
break;
case LHP_ELEM_IMG:
pname = lws_html_get_atr(ps, "src", 3);
if (!psb)
break;
if (ps->css_position->propval == LCSP_PROPVAL_ABSOLUTE) {
box.x = *lws_csp_px(ps->css_pos[CCPAS_LEFT], ps);
box.y = *lws_csp_px(ps->css_pos[CCPAS_TOP], ps);
} else {
box.x = psb->curx;
box.y = psb->cury;
}
lws_fx_set(box.x, 0, 0);
lws_fx_set(box.y, 0, 0);
if (psb) {
lws_fx_add(&box.x, &box.x,
lws_csp_px(psb->css_margin[CCPAS_LEFT], psb));
lws_fx_add(&box.y, &box.y,
lws_csp_px(psb->css_margin[CCPAS_TOP], psb));
}
box.h = ctx->ic.wh_px[1]; /* placeholder */
lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x);
if (ps->css_width &&
lws_fx_comp(lws_csp_px(ps->css_width, ps), &box.w) > 0)
box.w = *lws_csp_px(ps->css_width, ps);
if (lws_http_rel_to_url(url, sizeof(url),
ctx->base_url, pname))
break;
if (lws_dlo_ss_find(cx, url, &u))
break;
lws_lhp_tag_dlo_id(ctx, ps, (lws_dlo_t *)(u.u.dlo_jpeg));
w = *lws_csp_px(lws_css_cascade_get_prop_atr(ctx,
LCSP_PROP_WIDTH), ps);
h = *lws_csp_px(lws_css_cascade_get_prop_atr(ctx,
LCSP_PROP_HEIGHT), ps);
if (!w.whole || !h.whole) {
w = ((lws_dlo_t *)(u.u.dlo_jpeg))->box.w;
h = ((lws_dlo_t *)(u.u.dlo_jpeg))->box.w;
}
if (psb) {
lws_fx_add(&psb->curx, &psb->curx, &w);
lws_fx_add(&psb->cury, &psb->cury, &h);
psb->dlo_set_curx = ps->dlo;
psb->dlo_set_cury = ps->dlo;
if (lws_fx_comp(&psb->curx, &psb->widest) > 0)
psb->widest = psb->curx;
}
break;
default:
break;
}
break;
case LHPCB_ELEMENT_END:
if (ctx->npos == 2 && ctx->buf[0] == 'h' &&
ctx->buf[1] > '0' && ctx->buf[1] <= '6') {
if (!psb)
break;
newline(ctx, psb, ps, drt->dl);
lws_fx_add(&psb->cury, &psb->cury,
lws_csp_px(ps->css_padding[CCPAS_BOTTOM], ps));
lws_fx_add(&psb->cury, &psb->cury,
lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps));
break;
}
switch (elem_match) {
case LHP_ELEM_TR:
pst = ps;
while (pst && !pst->is_table)
pst = lws_css_get_parent_block(ctx, pst);
if (!pst) {
lwsl_err("%s: /td: no table\n", __func__);
break;
}
pst->tr_idx++;
pst->td_idx = 0;
goto do_end_rect;
case LHP_ELEM_TD:
pst = ps;
while (pst && !pst->is_table)
pst = lws_css_get_parent_block(ctx, pst);
if (!pst) {
lwsl_err("%s: /td: no table\n", __func__);
break;
}
pst->td_idx++;
goto do_end_rect;
/* fallthru */
case LHP_ELEM_TABLE:
case LHP_ELEM_DIV:
do_end_rect:
ox = ps->curx;
if (lws_fx_comp(&ox, &ps->widest) > 0)
ps->widest = ox;
newline(ctx, ps, ps, drt->dl);
if (lws_lhp_dlo_adjust_div_type_element(ctx, psb, pst, ps, elem_match))
break;
if (lws_fx_comp(&ps->curx, &ps->widest) > 0)
ps->widest = ps->curx;
/* move parent on according to used area plus bottom margin */
if (psb && ps->css_position->propval != LCSP_PROPVAL_ABSOLUTE) {
switch (ps->css_display->propval) {
case LCSP_PROPVAL_BLOCK:
case LCSP_PROPVAL_TABLE:
case LCSP_PROPVAL_TABLE_ROW:
lws_fx_set(psb->curx, 0, 0);
psb->dlo_set_curx = NULL;
if (ps->css_display->propval == LCSP_PROPVAL_TABLE_ROW)
break;
lws_fx_add(&psb->cury, &psb->cury, lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps));
break;
case LCSP_PROPVAL_INLINE_BLOCK:
//lws_fx_add(&psb->cury, &psb->cury, lws_csp_px(ps->css_margin[CCPAS_BOTTOM], ps));
lws_fx_add(&psb->curx, &psb->curx, &ps->widest);
lws_fx_add(&psb->curx, &psb->curx, lws_csp_px(ps->css_margin[CCPAS_RIGHT], ps));
lws_fx_set(ps->widest, 0, 0);
psb->dlo_set_curx = ps->dlo;
psb->dlo_set_cury = ps->dlo;
break;
default:
lws_fx_add(&psb->curx, &psb->curx, &ps->widest);
psb->dlo_set_curx = ps->dlo;
break;
}
if (lws_fx_comp(&psb->curx, &psb->widest) > 0)
psb->widest = psb->curx;
}
ps->dlo = NULL;
break;
default:
break;
}
break;
case LHPCB_CONTENT:
if (!ps->css_display ||
ps->css_display->propval == LCSP_PROPVAL_NONE)
break;
if (ps->css_color)
col = ps->css_color->u.rgba;
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_SIZE);
if (a)
fc.fixed_height = (uint16_t)a->u.i.whole;
a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_FONT_FAMILY);
if (a)
fc.family_name = (const char *)&a[1];
for (n = 0; n < ctx->npos; n++)
if (ctx->buf[n] == '\n')
s++;
if (s == ctx->npos)
return 0;
/*
* Let's not deal with things off the bottom of the display
* surface.
*/
if (psb && psb->cury.whole > ctx->ic.wh_px[LWS_LHPREF_HEIGHT].whole)
return 0;
if (!psb)
return 0;
f = lws_font_choose(cx, &fc);
n = s;
while (n < ctx->npos) {
int m;
lws_fx_set(box.x, 0, 0);
lws_fx_set(box.y, 0, 0);
lws_fx_set(box.w, 0, 0);
if (n == s && !(psb->runon & 1)) {
lws_fx_set(indent, 0, 0);
} else
indent = psb->curx;
lws_fx_add(&box.x, &indent,
lws_csp_px(ps->css_padding[CCPAS_LEFT], ps));
lws_fx_add(&box.y, &box.y, &psb->cury);
box.h.whole = (int32_t)f->choice.fixed_height;
box.h.frac = 0;
if (psb->css_width &&
(psb->css_width->propval == LCSP_PROPVAL_AUTO ||
ps->css_width->propval == LCSP_PROPVAL_AUTO)) {
//lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x);
box.w = ctx->ic.wh_px[0];
} else {
lws_fx_sub(&t1, &psb->drt.w,
lws_csp_px(psb->css_padding[CCPAS_LEFT], psb));
lws_fx_sub(&box.w, &t1,
lws_csp_px(psb->css_padding[CCPAS_RIGHT], psb));
}
if (!box.w.whole)
lws_fx_sub(&box.w, &ctx->ic.wh_px[0], &box.x);
assert(psb);
txt = lws_display_dlo_text_new(drt->dl,
(lws_dlo_t *)psb->dlo, &box, f);
if (!txt) {
lwsl_err("%s: failed to alloc text\n", __func__);
return 1;
}
runon(psb, &txt->dlo);
txt->flags |= LWSDLO_TEXT_FLAG_WRAP;
lhp_set_dlo_padding_margin(ps, &txt->dlo);
// a = lws_css_cascade_get_prop_atr(ctx, LCSP_PROP_TEXT_ALIGN);
//lwsl_hexdump_notice(ctx->buf + n, (size_t)(ctx->npos - n));
m = lws_display_dlo_text_update(txt, col, indent,
ctx->buf + n,
(size_t)(ctx->npos - n));
if (m < 0) {
lwsl_err("text_update ret %d\n", m);
break;
}
if (m == 2 && lastm)
return 0;
lastm = m == 2;
n = (int)((size_t)n + txt->text_len);
txt->dlo.box.w = txt->bounding_box.w;
txt->dlo.box.h = txt->bounding_box.h;
lws_fx_add(&psb->curx, &psb->curx, &txt->bounding_box.w);
psb->dlo_set_curx = &txt->dlo;
//lwsl_user("%s: bounding width %d, m: %d, text %.*s\n",
// __func__, txt->bounding_box.w.whole, m,
// ctx->npos, ctx->buf);
if (m > 0) { /* wrapping */
newline(ctx, psb, ps, drt->dl);
lws_fx_set(ps->curx, 0, 0);
lws_fx_set(psb->curx, 0, 0);
psb->dlo_set_curx = NULL;
lws_fx_add(&ps->cury, &ps->cury, &txt->bounding_box.h);
psb->dlo_set_cury = &txt->dlo;
}
}
break;
case LHPCB_COMMENT:
break;
}
return 0;
}

View File

@ -0,0 +1,184 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: PNG
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
void
lws_display_dlo_png_destroy(struct lws_dlo *dlo)
{
lws_dlo_png_t *dlo_png = lws_container_of(dlo, lws_dlo_png_t, dlo);
#if defined(LWS_WITH_CLIENT) && defined(LWS_WITH_SECURE_STREAMS)
lws_ss_destroy(&dlo_png->flow.h);
#endif
lws_buflist_destroy_all_segments(&dlo_png->flow.bl);
if (dlo_png->png)
lws_upng_free(&dlo_png->png);
}
lws_stateful_ret_t
lws_display_render_png(struct lws_display_render_state *rs)
{
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_dlo_png_t *dlo_png = lws_container_of(dlo, lws_dlo_png_t, dlo);
lws_fx_t ax, ay, t, t1;
lws_display_colour_t pc;
lws_stateful_ret_t r;
const uint8_t *pix;
int s, e;
if (!lws_upng_get_height(dlo_png->png)) {
lwsl_info("%s: png does not have dimensions yet\n", __func__);
return LWS_SRET_WANT_INPUT;
}
lws_fx_add(&ax, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&t, &ax, &dlo->box.w);
lws_fx_add(&ay, &rs->st[rs->sp].co.y, &dlo->box.y);
lws_fx_add(&t1, &ay, &dlo->box.h);
s = ax.whole;
e = lws_fx_roundup(&t);
if (rs->curr > lws_fx_roundup(&t1))
return LWS_SRET_OK;
if (rs->curr - lws_fx_roundup(&ay) >
(int)lws_upng_get_height(dlo_png->png))
return LWS_SRET_OK;
if (s < 0)
s = 0;
if (s > rs->ic->wh_px[0].whole)
return LWS_SRET_OK; /* off to the right */
if (e > rs->ic->wh_px[0].whole)
e = rs->ic->wh_px[0].whole - 1;
if (e <= 0)
return LWS_SRET_OK; /* off to the left */
do {
if (lws_flow_feed(&dlo_png->flow))
/* if he says WANT_INPUT, we have nothing in the buflist */
return LWS_SRET_WANT_INPUT;
pix = NULL;
r = lws_upng_emit_next_line(dlo_png->png, &pix, &dlo_png->flow.data,
&dlo_png->flow.len, rs->html == 1 /* hold at metadata */);
if (r & LWS_SRET_NO_FURTHER_IN)
dlo_png->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED;
if (r & (LWS_SRET_FATAL | LWS_SRET_YIELD) || r == LWS_SRET_OK)
return r;
r = lws_flow_req(&dlo_png->flow);
if (r & LWS_SRET_WANT_INPUT)
return r;
} while (!pix);
pix = pix + (( (unsigned int)(s - ax.whole) *
(lws_upng_get_pixelsize(dlo_png->png) / 8)));
while (s < e && s >= ax.whole && s < lws_fx_roundup(&t) &&
(s - ax.whole) < (int)lws_upng_get_width(dlo_png->png)) {
if (lws_upng_get_pixelsize(dlo_png->png))
pc = LWSDC_RGBA(pix[0], pix[0], pix[0], pix[1]);
pc = LWSDC_RGBA(pix[0], pix[1], pix[2], pix[3]);
lws_surface_set_px(rs->ic, rs->line, s, &pc);
s++;
pix += lws_upng_get_pixelsize(dlo_png->png) / 8;
}
return LWS_SRET_OK;
}
lws_stateful_ret_t
lws_display_dlo_png_metadata_scan(lws_dlo_png_t *dlo_png)
{
lws_stateful_ret_t r;
size_t l, l1;
const uint8_t *pix;
/*
* If we don't have the image metadata yet, provide small chunks of the
* source data until we do have the image metadata, but small enough
* we can't produce any decoded pixels too early.
*/
while (!lws_upng_get_height(dlo_png->png) && dlo_png->flow.len) {
l1 = l = dlo_png->flow.len > 33 ? 33 : dlo_png->flow.len;
r = lws_upng_emit_next_line(dlo_png->png, &pix, &dlo_png->flow.data, &l, 1);
if (r & LWS_SRET_FATAL) {
lwsl_err("%s: hdr parse failed\n", __func__);
return r;
}
dlo_png->flow.len -= l1 - l;
if (lws_upng_get_height(dlo_png->png)) {
lwsl_info("png: w %d, h %d\n",
lws_upng_get_width(dlo_png->png),
lws_upng_get_height(dlo_png->png));
return LWS_SRET_OK;
}
}
return LWS_SRET_WANT_INPUT;
}
lws_dlo_png_t *
lws_display_dlo_png_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box)
{
lws_dlo_png_t *dlo_png = lws_zalloc(sizeof(*dlo_png), __func__);
dlo_png->png = lws_upng_new();
if (!dlo_png->png)
goto bail;
dlo_png->dlo.box = *box;
dlo_png->dlo.render = lws_display_render_png;
dlo_png->dlo._destroy = lws_display_dlo_png_destroy;
lws_display_dlo_add(dl, dlo_parent, &dlo_png->dlo);
return dlo_png;
bail:
if (dlo_png->png)
lws_upng_free(&dlo_png->png);
lws_free(dlo_png);
return NULL;
}

View File

@ -0,0 +1,247 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: rect / rounded rect
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
/* returns where on the x axis we intercept ys (== (curr - ory) ^ 2 ) */
static void
isect(lws_circle_t *c, lws_fx_t *f, lws_fx_t *axsq)
{
assert(axsq->whole >= 0);
assert(c->rsq.whole >= 0);
lws_fx_sub(f, &c->rsq, axsq);
if (f->whole < 0) {
f->whole = 0;
f->frac = 0;
} else
lws_fx_sqrt(f, f);
lws_fx_sub(f, &c->r, f);
}
/* give it absolute x, returns intersection point as absolute y*/
static void
isect_y_from_x(lws_circle_t *c, lws_fx_t *x, lws_fx_t *y)
{
lws_fx_t t, t1;
lws_fx_sub(y, x, &c->orx);
lws_fx_mul(&t, y, y);
lws_fx_sub(&t1, &c->rsq, &t);
lws_fx_sqrt(&t, &t1);
lws_fx_add(y, &c->ory, &t);
}
lws_stateful_ret_t
lws_display_render_rect(struct lws_display_render_state *rs)
/* const lws_surface_info_t *ic, struct lws_dlo *dlo,
const lws_box_t *origin, lws_display_scalar curr,
uint8_t *line, lws_colour_error_t **nle) */
{
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_dlo_rect_t *r = lws_container_of(dlo, lws_dlo_rect_t, dlo);
lws_fx_t cf, y, w, trim, s, e, t2, sfy;
lws_display_colour_t dc;
int n, le, os;
if (!LWSDC_ALPHA(dlo->dc))
return LWS_SRET_OK;
if (!r->init) {
lws_fx_add(&r->db.x, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&r->db.y, &rs->st[rs->sp].co.y, &dlo->box.y);
lws_fx_add(&r->right, &r->db.x, &dlo->box.w);
lws_fx_add(&r->btm, &r->db.y, &dlo->box.h);
lws_fx_add(&r->c[0].ory, &r->db.y, &r->c[0].r);
lws_fx_add(&r->c[1].ory, &r->db.y, &r->c[1].r);
lws_fx_sub(&r->c[2].ory, &r->btm, &r->c[2].r);
lws_fx_sub(&r->c[3].ory, &r->btm, &r->c[3].r);
lws_fx_add(&r->c[0].orx, &r->db.x, &r->c[0].r);
lws_fx_sub(&r->c[1].orx, &r->right, &r->c[1].r);
lws_fx_add(&r->c[2].orx, &r->db.x, &r->c[2].r);
lws_fx_sub(&r->c[3].orx, &r->right, &r->c[3].r);
r->init = 1;
}
if (lws_fx_comp(&r->db.x, &rs->ic->wh_px[0]) >= 0)
return LWS_SRET_OK; /* off to the right */
if (rs->curr < r->db.y.whole - 1 || rs->curr > lws_fx_roundup(&r->btm))
return LWS_SRET_OK;
s = r->db.x;
lws_fx_add(&e, &s, &dlo->box.w);
cf.whole = rs->curr;
cf.frac = 50000000;
/*
* Account for four independently radiused corners
*
* Fractional pixel occupancy is represented by modulating alpha.
*
* We know that the subpixel intersection on the circle is at yo.frac +
* radius.frac which usually won't align to any pixel boundary.
*/
for (n = 0; n < 4; n++) {
lws_fx_sub(&y, &cf, &r->c[n].ory);
lws_fx_mul(&r->c[n].ys, &y, &y);
}
/* For this y line, find out how many x pixels we can skip at start
* and end before and after the first pixels that intersect */
if (rs->curr <= (r->c[0].ory.whole)) { /* top left trims s */
isect(&r->c[0], &trim, &r->c[0].ys /* (cf - ory)^2 */);
lws_fx_add(&s, &s, &trim);
}
if (rs->curr <= (r->c[1].ory.whole)) { /* top right trims e */
isect(&r->c[1], &trim, &r->c[1].ys);
lws_fx_sub(&e, &e, &trim);
}
if (rs->curr >= (r->c[2].ory.whole)) { /* bottom left trims s */
isect(&r->c[2], &trim, &r->c[2].ys);
lws_fx_add(&s, &s, &trim);
}
if (rs->curr >= (r->c[3].ory.whole)) { /* bottom right trims e */
isect(&r->c[3], &trim, &r->c[3].ys);
lws_fx_sub(&e, &e, &trim);
}
/* clips */
if (s.whole < 0)
lws_fx_set(s, 0, 0);
if (e.whole >= rs->ic->wh_px[0].whole)
lws_fx_set(e, rs->ic->wh_px[0].whole - 1, 0);
if (e.whole <= 0 || e.whole < s.whole)
return LWS_SRET_OK; /* off to the left */
lws_fx_sub(&w, &e, &s);
if (lws_fx_comp(&w, &dlo->box.w) > 0)
lws_fx_add(&e, &s, &dlo->box.w);
/* render the part of the line occupied by the rect body */
sfy = s;
os = s.whole;
s.frac = 0;
le = e.whole + 1;
while (s.whole <= le) {
unsigned int alpha = dlo->dc >> 24;
if (rs->curr <= r->c[0].ory.whole - 1 && s.whole >= r->db.x.whole &&
lws_fx_comp(&s, &r->c[0].orx) <= 0) {
isect_y_from_x(&r->c[0], &s, &t2);
lws_fx_sub(&t2, &t2, &r->c[0].r);
lws_fx_sub(&t2, &t2, &r->c[0].r);
if (t2.frac && lws_fx_rounddown(&t2) == rs->curr)
alpha = (((uint64_t)t2.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
}
if (rs->curr <= (r->c[1].ory.whole - 1) &&
s.whole >= r->c[1].orx.whole) {
isect_y_from_x(&r->c[1], &s, &t2);
lws_fx_sub(&t2, &t2, &r->c[1].r);
lws_fx_sub(&t2, &t2, &r->c[1].r);
if (t2.frac && lws_fx_rounddown(&t2) == rs->curr)
alpha = (((uint64_t)t2.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
}
if (rs->curr >= (r->c[2].ory.whole + 1) &&
s.whole < lws_fx_roundup(&r->c[2].orx)) {
isect_y_from_x(&r->c[2], &s, &t2);
if (t2.frac && lws_fx_rounddown(&t2) == rs->curr)
alpha = (((uint64_t)t2.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
}
if (rs->curr >= (r->c[3].ory.whole + 1) &&
s.whole >= lws_fx_roundup(&r->c[3].orx)) {
isect_y_from_x(&r->c[3], &s, &t2);
if (t2.frac && lws_fx_rounddown(&t2) == rs->curr)
alpha = (((uint64_t)t2.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
}
if (s.whole == os && sfy.frac)
alpha = (((uint64_t)(99999999 - sfy.frac) * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
if (s.whole == le)
alpha = (((uint64_t)e.frac * alpha) /
LWS_FX_FRACTION_MSD) & 0xff;
dc = (lws_display_colour_t)(((dlo->dc & 0xffffff) |
(uint32_t)(alpha << 24)));
lws_surface_set_px(rs->ic, rs->line, s.whole, &dc);
s.whole++;
}
return LWS_SRET_OK;
}
lws_dlo_rect_t *
lws_display_dlo_rect_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box, const lws_fx_t *radii,
lws_display_colour_t dc)
{
lws_dlo_rect_t *r = lws_zalloc(sizeof(*r), __func__);
int n;
if (!r)
return NULL;
r->dlo.render = lws_display_render_rect;
r->dlo.box = *box;
r->dlo.dc = dc;
if (radii) {
r->c[0].r = radii[0];
r->c[1].r = radii[1];
r->c[2].r = radii[2];
r->c[3].r = radii[3];
for (n = 0; n < 4; n++)
lws_fx_mul(&r->c[n].rsq, &r->c[n].r, &r->c[n].r);
}
lws_display_dlo_add(dl, dlo_parent, &r->dlo);
return r;
}

View File

@ -0,0 +1,342 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Secure Streams as DLO transport
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
LWS_SS_USER_TYPEDEF
sul_cb_t on_rx;
lhp_ctx_t *lhp;
lws_sorted_usec_list_t *ssevsul; /* sul to use to resume rz */
lws_sorted_usec_list_t sul; /* used for initial metadata cb */
lws_dlo_image_t u; /* we use the lws_flow_t in here */
lws_dll2_t active_asset_list; /*cx->active_assets*/
uint8_t type; /* LWSDLOSS_TYPE_ */
char url[96];
} dloss_t;
/*
* dlo images call back here when they have their dimensions (or have failed)
*/
void
lws_lhp_image_dimensions_cb(lws_sorted_usec_list_t *sul)
{
dloss_t *m = lws_container_of(sul, dloss_t, sul);
lws_display_render_state_t *rs = lws_container_of(m->ssevsul,
lws_display_render_state_t, sul);
lws_dlo_dim_t dim;
lws_dlo_t *dlo = &m->u.u.dlo_png->dlo;
if (m->u.failed) {
dlo->box.w.whole = -1;
dlo->box.h.whole = -1;
lwsl_notice("%s: Failing %s\n", __func__, m->url);
} else {
dlo->box.w.whole = (int32_t)lws_dlo_image_width(&m->u);
dlo->box.h.whole = (int32_t)lws_dlo_image_height(&m->u);
lwsl_err("%s: setting dlo box %d x %d\n", __func__,
(int)dlo->box.w.whole, (int)dlo->box.h.whole);
#if 1
lws_dlo_contents(dlo, &dim);
lws_display_dlo_adjust_dims(dlo, &dim);
if (dlo->list.owner) {
dlo = lws_container_of(dlo->list.owner, lws_dlo_t, children);
lws_dlo_contents(dlo, &dim);
lws_display_dlo_adjust_dims(dlo, &dim);
}
#endif
}
if (rs->html != 1) {
lws_sul_schedule(lws_ss_get_context(m->ss), 0, m->ssevsul, m->on_rx, 1);
return;
}
/* we are resuming the html parsing */
lws_lhp_ss_html_parse_from_lhp(m->lhp);
}
/* secure streams payload interface */
static lws_ss_state_return_t
dloss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
{
dloss_t *m = (dloss_t *)userobj;
lws_stateful_ret_t r;
lwsl_info("%s: %u\n", __func__, (unsigned int)len);
if (m->type == LWSDLOSS_TYPE_CSS) {
m->lhp->finish_css = !!(flags & LWSSS_FLAG_EOM);
m->lhp->is_css = 1;
r = lws_lhp_parse(m->lhp, &buf, &len);
m->lhp->is_css = 0;
if (flags & LWSSS_FLAG_EOM)
lws_dll2_remove(&m->active_asset_list);
if (r & LWS_SRET_FATAL)
return LWSSSSRET_DISCONNECT_ME;
if (r & LWS_SRET_AWAIT_RETRY) {
lwsl_warn("%s: returning to await retry\n", __func__);
if (!m->lhp->await_css_done)
lws_sul_schedule(lws_ss_get_context(m->ss), 0,
m->lhp->sshtmlevsul,
m->lhp->sshtmlevcb, 1);
}
goto okie;
}
/* .flow is at the same offset in both dlo_jpeg and dlo_png */
if (len &&
lws_buflist_append_segment(&m->u.u.dlo_jpeg->flow.bl, buf, len) < 0) {
m->u.failed = 1;
lws_sul_schedule(lws_ss_get_context(m->ss), 0,
&m->sul, lws_lhp_image_dimensions_cb, 1);
return LWSSSSRET_DISCONNECT_ME;
}
// lwsl_notice("%s: buflen size %d\n", __func__,
// (int)lws_buflist_total_len(&m->u.u.dlo_jpeg->flow.bl));
if (flags & LWSSS_FLAG_EOM) {
m->u.u.dlo_jpeg->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED;
return LWSSSSRET_DISCONNECT_ME;
}
if (!lws_dlo_image_width(&m->u)) {
lws_flow_feed(&m->u.u.dlo_jpeg->flow);
r = lws_dlo_image_metadata_scan(&m->u);
lws_flow_req(&m->u.u.dlo_jpeg->flow);
if (r & LWS_SRET_FATAL) {
m->u.failed = 1;
lws_sul_schedule(lws_ss_get_context(m->ss), 0,
&m->sul, lws_lhp_image_dimensions_cb, 1);
return LWSSSSRET_DISCONNECT_ME;
}
if (r != LWS_SRET_WANT_INPUT) {
lwsl_notice("%s: seen metadata\n", __func__);
lws_sul_schedule(lws_ss_get_context(m->ss), 0,
&m->sul, lws_lhp_image_dimensions_cb, 1);
} //else
//lwsl_err("%s: metadata scan no end yet\n", __func__);
return LWSSSSRET_OK;
}
okie:
lws_sul_schedule(lws_ss_get_context(m->ss), 0, m->ssevsul, m->on_rx, 1);
return LWSSSSRET_OK;
}
static lws_ss_state_return_t
dloss_state(void *userobj, void *sh, lws_ss_constate_t state,
lws_ss_tx_ordinal_t ack)
{
dloss_t *m = (dloss_t *)userobj;
switch (state) {
case LWSSSCS_CREATING:
break;
case LWSSSCS_DESTROYING:
lws_sul_cancel(&m->sul);
lws_dll2_remove(&m->active_asset_list);
break;
default:
break;
}
return LWSSSSRET_OK;
}
static LWS_SS_INFO("__default", dloss_t)
.rx = dloss_rx,
.state = dloss_state
};
/*
* If we have an active image asset from this URL, return a pointer to its
* dlo image (ie, dlo_jpeg or dlo_png)
*/
int
lws_dlo_ss_find(struct lws_context *cx, const char *url, lws_dlo_image_t *u)
{
lws_start_foreach_dll(struct lws_dll2 *, d,
lws_dll2_get_head(&cx->active_assets)) {
dloss_t *ds = lws_container_of(d, dloss_t, active_asset_list);
if (!strcmp(url, ds->url)) {
*u = ds->u;
return 0; /* found */
}
} lws_end_foreach_dll(d);
return 1; /* not found */
}
int
lws_dlo_ss_create(lws_dlo_ss_create_info_t *i, lws_dlo_t **pdlo)
{
lws_dlo_jpeg_t *dlo_jpeg = NULL;
lws_dlo_png_t *dlo_png = NULL;
size_t ul = strlen(i->url);
struct lws_ss_handle *h;
lws_dlo_t *dlo = NULL;
lws_ss_info_t ssi;
dloss_t *dloss;
uint8_t type;
if (ul < 5)
return 1;
if (!strcmp(i->url + ul - 4, ".png"))
type = LWSDLOSS_TYPE_PNG;
else
if (!strcmp(i->url + ul - 4, ".jpg") ||
!strcmp(i->url + ul - 5, ".jpeg"))
type = LWSDLOSS_TYPE_JPEG;
else
if (!strcmp(i->url + ul - 4, ".css"))
type = LWSDLOSS_TYPE_CSS;
else {
lwsl_err("%s: unknown file type %s\n", __func__, i->url);
return 1;
}
switch (type) {
case LWSDLOSS_TYPE_PNG:
dlo_png = lws_display_dlo_png_new(i->dl, i->dlo_parent, i->box);
if (!dlo_png)
return 1;
i->u->u.dlo_png = dlo_png;
dlo_png->dlo.box.w.whole = (int32_t)
lws_upng_get_width(dlo_png->png);
dlo_png->dlo.box.w.frac = 0;
dlo_png->dlo.box.h.whole = (int32_t)
lws_upng_get_height(dlo_png->png);
dlo_png->dlo.box.h.frac = 0;
dlo = &dlo_png->dlo;
break;
case LWSDLOSS_TYPE_JPEG:
dlo_jpeg = lws_display_dlo_jpeg_new(i->dl, i->dlo_parent, i->box);
if (!dlo_jpeg)
return 1;
i->u->u.dlo_jpeg = dlo_jpeg;
dlo_jpeg->dlo.box.w.whole = (int32_t)
lws_jpeg_get_width(dlo_jpeg->j);
dlo_jpeg->dlo.box.w.frac = 0;
dlo_jpeg->dlo.box.h.whole = (int32_t)
lws_jpeg_get_height(dlo_jpeg->j);
dlo_jpeg->dlo.box.h.frac = 0;
dlo = &dlo_jpeg->dlo;
break;
}
/* we adapt the initial tx credit also to the requested window */
ssi = ssi_dloss_t;
ssi.manual_initial_tx_credit = i->window;
if (lws_ss_create(i->cx, 0, &ssi, (void *)dlo, &h, NULL, NULL)) {
lwsl_notice("%s: unable to create ss\n", __func__);
return 1;
}
dloss = (dloss_t *)lws_ss_to_user_object(h);
dloss->u.type = (lws_dlo_image_type_t)type;
dloss->on_rx = i->on_rx;
dloss->ssevsul = i->on_rx_sul;
dloss->lhp = i->lhp;
dloss->type = type;
lws_strncpy(dloss->url, i->url, sizeof(dloss->url));
switch (type) {
case LWSDLOSS_TYPE_PNG:
dloss->u.u.dlo_png = dlo_png;
dlo_png->flow.h = h;
dlo_png->flow.window = i->window;
break;
case LWSDLOSS_TYPE_JPEG:
dloss->u.u.dlo_jpeg = dlo_jpeg;
dlo_jpeg->flow.h = h;
dlo_jpeg->flow.window = i->window;
break;
}
if (lws_ss_alloc_set_metadata(h, "endpoint", i->url, ul)) {
lwsl_err("%s: unable to set endpoint\n", __func__);
goto fail;
}
if (lws_ss_client_connect(dloss->ss)) {
lwsl_err("%s: unable to do client connection\n", __func__);
goto fail;
}
lws_dll2_add_tail(&dloss->active_asset_list, &i->cx->active_assets);
lwsl_notice("%s: starting %s (dlo %p)\n", __func__, i->url, dlo);
*pdlo = dlo;
return 0;
fail:
lws_ss_destroy(&h);
switch (type) {
case LWSDLOSS_TYPE_PNG:
lws_display_dlo_png_destroy(&dlo_png->dlo);
break;
case LWSDLOSS_TYPE_JPEG:
lws_display_dlo_jpeg_destroy(&dlo_jpeg->dlo);
break;
}
return 1;
}

View File

@ -0,0 +1,413 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object: text
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
size_t
utf8_bytes(uint8_t u)
{
if ((u & 0x80) == 0)
return 1;
if ((u & 0xe0) == 0xc0)
return 2;
if ((u & 0xf0) == 0xe0)
return 3;
if ((u & 0xf8) == 0xf0)
return 4;
return 0;
}
static int
utf8_unicode(const char *utf8, size_t *utf8_len, uint32_t *unicode)
{
size_t glyph_len = utf8_bytes((uint8_t)*utf8);
size_t n;
if (!glyph_len || glyph_len > *utf8_len) {
(*utf8_len)--;
return 1;
}
if (glyph_len == 1)
*unicode = (uint32_t)*utf8++;
else {
*unicode = (uint32_t)((*utf8++) & (0x7f >> glyph_len));
for (n = 1; n < glyph_len; n++)
*unicode = (*unicode << 6) | ((*utf8++) & 0x3f);
}
*utf8_len -= glyph_len;
return 0;
}
void
lws_display_dlo_text_destroy(struct lws_dlo *dlo)
{
lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo);
lws_free_set_NULL(text->kern);
lws_free_set_NULL(text->text);
lwsac_free(&text->ac_glyphs);
}
int
lws_display_dlo_text_update(lws_dlo_text_t *text, lws_display_colour_t dc,
lws_fx_t indent, const char *utf8, size_t text_len)
{
const char *last_utf8 = utf8, *outf8 = utf8;
size_t last_bp_n = 0, tlen = text_len;
lws_fx_t t1, eff, last_bp_eff, t2;
uint8_t r = 0;
char uc;
if (text->kern)
lws_free_set_NULL(text->kern);
if (text->text)
lws_free_set_NULL(text->text);
lws_dll2_owner_clear(&text->glyphs);
lwsac_free(&text->ac_glyphs);
text->indent = indent;
text->dlo.dc = dc;
lws_fx_set(eff, 0, 0);
/*
* Let's go through the new string glyph by glyph, we want to
* calculate effective kerned widths, and optionally deal with wrapping.
*
* But we don't want to instantiate the glyph objects until we are
* engaged with rendering them. Otherwise we will carry around the
* whole page-worth's of glyphs at once needlessly, which won't scale
* for text-heavy pages. lws_display_dlo_text_attach_glyphs() does the
* same flow as this but to create the glyphs and is called later
* as the text dlo becomes rasterized during rendering.
*/
/* { char b1[22]; lwsl_err("eff %s\n", lws_fx_string(&eff, b1, sizeof(b1))); }
{ char b1[22]; lwsl_err("indent %s\n", lws_fx_string(&indent, b1, sizeof(b1))); }
{ char b1[22]; lwsl_err("boxw %s\n", lws_fx_string(&text->dlo.box.w, b1, sizeof(b1))); } */
while (tlen &&
lws_fx_comp(lws_fx_add(&t1, &eff, &indent), &text->dlo.box.w) < 0) {
size_t ot = tlen;
uint32_t unicode;
if (!utf8_unicode(utf8, &tlen, &unicode)) {
text->font->image_glyph(text, unicode, 0);
uc = *utf8;
utf8 += (ot - tlen);
if (uc == ' ') { /* act to snip it if used */
last_utf8 = utf8;
last_bp_n = tlen;
last_bp_eff = eff;
}
if (!lws_display_font_mcufont_getcwidth(text, unicode, &t2))
lws_fx_add(&eff, &eff, &t2);
if (uc == '-' || uc == ',' || uc == ';' || uc == ':') {
/* act to leave it in */
last_utf8 = utf8;
last_bp_n = tlen;
last_bp_eff = eff;
}
} else
lwsl_err("%s: missing glyph\n", __func__);
}
if (last_bp_n &&
lws_fx_comp(lws_fx_add(&t1, &eff, &indent), &text->dlo.box.w) >= 0) {
eff = last_bp_eff;
utf8 = last_utf8;
tlen = last_bp_n;
r = 1;
}
text->text_len = text_len - tlen;
if (tlen == text_len) {
lwsl_notice("we couldn't fit anything in there, newline\n");
return 2;
}
text->text = lws_malloc(text->text_len + 1, __func__);
if (!text->text)
return -1;
memcpy(text->text, outf8, text->text_len);
text->text[text->text_len] = '\0';
memset(&text->bounding_box, 0, sizeof(text->bounding_box));
text->bounding_box.w = eff;
text->bounding_box.h.whole = text->font_height;
text->bounding_box.h.frac = 0;
return r;
}
int
lws_display_dlo_text_attach_glyphs(lws_dlo_text_t *text)
{
const char *utf8 = text->text;
size_t tlen = text->text_len;
lws_font_glyph_t *g = NULL;
uint32_t unicode;
lws_fx_t eff;
uint8_t r = 0;
lws_fx_set(eff, 0, 0);
while (tlen) {
size_t ot = tlen;
g = NULL;
if (!utf8_unicode(utf8, &tlen, &unicode))
/* instantiate the glyphs this time */
g = text->font->image_glyph(text, unicode, 1);
if (g == NULL) {
lwsl_warn("%s: no glyph for 0x%02X '%c'\n", __func__, (unsigned int)*utf8, *utf8);
break;
}
utf8 += (ot - tlen);
g->xpx = eff;
lws_fx_add(&eff, &eff, &g->cwidth);
}
return r;
}
lws_dlo_text_t *
lws_display_dlo_text_new(lws_displaylist_t *dl, lws_dlo_t *dlo_parent,
lws_box_t *box, const lws_display_font_t *font)
{
lws_dlo_text_t *text = lws_zalloc(sizeof(*text), __func__);
if (!text)
return NULL;
text->dlo.render = font->renderer;
text->dlo._destroy = lws_display_dlo_text_destroy;
text->dlo.box = *box;
text->font = font;
lws_display_dlo_add(dl, dlo_parent, &text->dlo);
return text;
}
static const char *
castrstr(const char *haystack, const char *needle)
{
size_t sn = strlen(needle), h = strlen(haystack) - sn + 1, n;
char c, c1;
while (1) {
for (n = 0; n < sn; n++) {
c = (char)((haystack[h + n] >= 'A' && haystack[h + n] <= 'Z') ?
haystack[h + n] + ('a' - 'A') : haystack[h + n]);
c1 = (char)((needle[n] >= 'A' && needle[n] <= 'Z') ?
needle[n] + ('a' - 'A') : needle[n]);
if (c != c1)
break;
}
if (n == sn)
return &haystack[h];
if (!h)
break;
h--;
}
return NULL;
}
int
lws_font_register(struct lws_context *cx, const uint8_t *data, size_t data_len)
{
lws_display_font_t *a;
if (lws_ser_ru32be(data) != LWS_FOURCC('M', 'C', 'U', 'F'))
return 1;
a = lws_zalloc(sizeof(*a), __func__);
if (!a)
return 1;
a->choice.family_name = (const char *)data +
lws_ser_ru32be(data + MCUFO_FOFS_FULLNAME);
if (castrstr(a->choice.family_name, "serif") ||
castrstr(a->choice.family_name, "roman"))
a->choice.generic_name = "serif";
else
a->choice.generic_name = "sans";
if (castrstr(a->choice.family_name, "italic") ||
castrstr(a->choice.family_name, "oblique"))
a->choice.style = 1;
if (castrstr(a->choice.family_name, "extrabold") ||
castrstr(a->choice.family_name, "extra bold"))
a->choice.weight = 900;
else
if (castrstr(a->choice.family_name, "bold"))
a->choice.weight = 700;
else
if (castrstr(a->choice.family_name, "extralight") ||
castrstr(a->choice.family_name, "extra light"))
a->choice.weight = 200;
else
if (castrstr(a->choice.family_name, "light"))
a->choice.weight = 300;
else
a->choice.weight = 400;
a->choice.fixed_height = lws_ser_ru16be(data + MCUFO16_LINE_HEIGHT);
a->data = data;
a->data_len = data_len;
a->renderer = lws_display_font_mcufont_render;
a->image_glyph = lws_display_font_mcufont_image_glyph;
{
lws_dlo_text_t t;
memset(&t, 0, sizeof(t));
t.font = a;
lws_display_font_mcufont_getcwidth(&t, 'm', &a->em);
a->ex.whole = a->choice.fixed_height;
a->ex.frac = 0;
}
lws_dll2_clear(&a->list);
lws_dll2_add_tail(&a->list, &cx->fonts);
return 0;
}
static int
lws_font_destroy(struct lws_dll2 *d, void *user)
{
lws_free(d);
return 0;
}
void
lws_fonts_destroy(struct lws_context *cx)
{
lws_dll2_foreach_safe(&cx->fonts, NULL, lws_font_destroy);
}
struct track {
const lws_font_choice_t *hints;
const lws_display_font_t *best;
int best_score;
};
static int
lws_fonts_score(struct lws_dll2 *d, void *user)
{
const lws_display_font_t *f = lws_container_of(d, lws_display_font_t,
list);
struct track *t = (struct track *)user;
struct lws_tokenize ts;
int score = 1000;
if (t->hints->family_name) {
memset(&ts, 0, sizeof(ts));
ts.start = t->hints->family_name;
ts.len = strlen(ts.start);
ts.flags = LWS_TOKENIZE_F_COMMA_SEP_LIST;
do {
ts.e = (int8_t)lws_tokenize(&ts);
if (ts.e == LWS_TOKZE_TOKEN) {
if (!strncmp(f->choice.family_name, ts.token,
ts.token_len)) {
score = 0;
break;
}
if (f->choice.generic_name &&
!strncmp(f->choice.generic_name, ts.token,
ts.token_len)) {
score -= 500;
break;
}
}
} while (ts.e > 0);
}
if (t->hints->weight)
score += (t->hints->weight > f->choice.weight ?
(t->hints->weight - f->choice.weight) :
(f->choice.weight - t->hints->weight)) / 100;
if (t->hints->style != f->choice.style)
score += 100;
if (t->hints->fixed_height)
score += 10 * (t->hints->fixed_height > f->choice.fixed_height ?
(t->hints->fixed_height - f->choice.fixed_height) :
(f->choice.fixed_height - t->hints->fixed_height));
if (score < t->best_score) {
t->best_score = score;
t->best = f;
}
return 0;
}
const lws_display_font_t *
lws_font_choose(struct lws_context *cx, const lws_font_choice_t *hints)
{
struct track t;
t.hints = hints;
t.best = (const lws_display_font_t *)cx->fonts.head;
t.best_score = 99999999;
if (t.hints)
lws_dll2_foreach_safe(&cx->fonts, &t, lws_fonts_score);
return t.best;
}

View File

@ -0,0 +1,890 @@
/*
* lws abstract display
*
* Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Display List Object handling
*/
#include <private-lib-core.h>
#include "private-lib-drivers-display-dlo.h"
#define dlodump_loglevel LLL_NOTICE
#if (_LWS_ENABLED_LOGS & dlodump_loglevel)
#define lwsl_dlodump(...) _lws_log(dlodump_loglevel, __VA_ARGS__)
#else
#define lwsl_dlodump(...)
#endif
void
lws_display_dl_init(lws_displaylist_t *dl, lws_display_state_t *ds)
{
lws_dll2_owner_clear(&dl->dl);
dl->ds = ds;
}
int
lws_display_dlo_add(lws_displaylist_t *dl, lws_dlo_t *dlo_parent, lws_dlo_t *dlo)
{
if (!dlo_parent && !dl->dl.head) {
lws_dll2_add_tail(&dlo->list, &dl->dl);
return 0;
}
if (!dlo_parent) {
if (!dl->dl.head)
return 0;
dlo_parent = lws_container_of(dl->dl.head, lws_dlo_t, list);
}
lws_dll2_add_tail(&dlo->list, &dlo_parent->children);
return 0;
}
void
lws_surface_set_px(const lws_surface_info_t *ic, uint8_t *line, int x,
const lws_display_colour_t *c)
{
unsigned int alpha, ialpha;
lws_display_colour_t oc;
lws_display_colour_t y;
uint8_t rgb[3];
if (x < 0 || x >= ic->wh_px[0].whole)
return;
/*
* All alpha composition takes place at 8bpp grey or 24bpp
*/
if (ic->greyscale) {
/* line composition buffer is 8-bit Y per pixel */
oc = line[x];
alpha = LWSDC_ALPHA(*c);
ialpha = 255 - alpha;
y = RGB_TO_Y(LWSDC_R(*c), LWSDC_G(*c), LWSDC_B(*c));
line[x] = (uint8_t)(((y * alpha) / 255) +
((LWSDC_R(oc) * ialpha) / 255));
return;
}
/* line composition buffer is 24-bit RGB per pixel */
line += (ic->render_to_rgba ? 4 : 3) * x;
alpha = LWSDC_ALPHA(*c);
ialpha = 255 - alpha;
rgb[0] = (uint8_t)(((LWSDC_R(*c) * alpha) / 255) +
((line[0] * ialpha) / 255));
rgb[1] = (uint8_t)(((LWSDC_G(*c) * alpha) / 255) +
((line[1] * ialpha) / 255));
rgb[2] = (uint8_t)(((LWSDC_B(*c) * alpha) / 255) +
((line[2] * ialpha) / 255));
*line++ = rgb[0];
*line++ = rgb[1];
*line++ = rgb[2];
if (ic->render_to_rgba)
*line = 0xff;
}
/*
* Recursively find out the total width and height of the contents of a DLO
*/
void
lws_dlo_contents(lws_dlo_t *parent, lws_dlo_dim_t *dim)
{
lws_display_render_stack_t st[12]; /* DLO child stack */
lws_dll2_t *d;
lws_fx_t t1;
int sp = 0;
dim->w.whole = 0;
dim->w.frac = 0;
dim->h.whole = 0;
dim->h.frac = 0;
if (!parent)
return;
d = lws_dll2_get_head(&parent->children);
if (!d)
return;
memset(&st, 0, sizeof(st));
st[0].dlo = lws_container_of(d, lws_dlo_t, list);
st[0].co.w.whole = 0;
st[0].co.w.frac = 0;
st[0].co.h.whole = 0;
st[0].co.h.frac = 0;
/* We are collecting worst dlo->box.x + dlo->box.w and .y + .h */
while (sp || st[0].dlo) {
lws_dlo_t *dlo = st[sp].dlo;
if (!dlo) {
if (!sp) {
lwsl_err("%s: underflow\n", __func__);
return;
}
if (lws_fx_comp(&st[sp].co.w, &st[sp - 1].co.w) > 0)
st[sp - 1].co.w = st[sp].co.w;
if (lws_fx_comp(&st[sp].co.h, &st[sp - 1].co.h) > 0)
st[sp - 1].co.h = st[sp].co.h;
// lwsl_notice("sp %d: passing back w: %d, h: %d\n", sp, st[sp - 1].co.w.whole, st[sp - 1].co.h.whole);
sp--;
continue;
}
lws_fx_add(&t1, &dlo->box.w, &dlo->box.x);
// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_LEFT]);
lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_LEFT]);
// lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_RIGHT]);
// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_RIGHT]);
if (lws_fx_comp(&t1, &st[sp].co.w) > 0)
st[sp].co.w = t1;
lws_fx_add(&t1, &dlo->box.h, &dlo->box.y);
// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_TOP]);
lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_TOP]);
// lws_fx_add(&t1, &t1, &dlo->padding[CCPAS_BOTTOM]);
// lws_fx_add(&t1, &t1, &dlo->margin[CCPAS_BOTTOM]);
if (lws_fx_comp(&t1, &st[sp].co.h) > 0)
st[sp].co.h = t1;
d = dlo->list.next;
if (d)
st[sp].dlo = lws_container_of(d, lws_dlo_t, list);
else
st[sp].dlo = NULL;
/* go into any children */
if (dlo->children.head) {
if (++sp == LWS_ARRAY_SIZE(st)) {
lwsl_err("%s: DLO stack overflow\n", __func__);
return;
}
st[sp].dlo = lws_container_of(
dlo->children.head, lws_dlo_t, list);
st[sp].co.w.whole = 0;
st[sp].co.h.whole = 0;
st[sp].co.w.frac = 0;
st[sp].co.h.frac = 0;
}
}
dim->w = st[0].co.w;
dim->h = st[0].co.h;
if (parent->col_list.owner) {
lhp_table_col_t *tc = lws_container_of(parent->col_list.owner,
lhp_table_col_t, col_dlos);
if (lws_fx_comp(&dim->w, &tc->width) < 0) {
// lws_fx_add(&t1, &tc->width, &parent->padding[CCPAS_LEFT]);
// lws_fx_add(&dim->w, &tc->width, &parent->padding[CCPAS_RIGHT]);
dim->w = tc->width;
}
}
if (parent->row_list.owner) {
lhp_table_row_t *tr = lws_container_of(parent->row_list.owner,
lhp_table_row_t, row_dlos);
if (lws_fx_comp(&dim->h, &tr->height) < 0) {
// lws_fx_add(&t1, &tr->height, &parent->padding[CCPAS_TOP]);
lws_fx_add(&dim->h, &tr->height, &parent->padding[CCPAS_BOTTOM]);
// dim->h = tr->height;
}
}
/*
lwsl_user("%s: dlo %p: FINAL w:%d -> %d h:%d -> %d\n", __func__, parent,
parent->box.w.whole, dim->w.whole,
parent->box.h.whole, dim->h.whole);
*/
}
/*
* Some DLO is changing height, adjust its height, and that of everybody below.
*/
void
lws_display_dlo_adjust_dims(lws_dlo_t *dlo, lws_dlo_dim_t *dim)
{
lws_dlo_dim_t delta;
if (!dim->w.whole && !dim->h.whole)
return;
/* adjust the target's width / height */
lws_fx_sub(&delta.w, &dim->w, &dlo->box.w);
lws_fx_sub(&delta.h, &dim->h, &dlo->box.h);
dlo->box.w = dim->w;
dlo->box.h = dim->h;
// lwsl_notice("%s: dlo %p: delta w:%d h:%d\n", __func__, dlo, delta.w.whole, delta.h.whole);
/* move peers below him accordingly */
do {
lws_dlo_t *dp = lws_container_of(dlo->list.owner, lws_dlo_t, children);
if (!dlo->list.owner)
break;
/*
* Adjust y pos of siblings below us
*/
do {
dlo = lws_container_of(dlo->list.next, lws_dlo_t, list);
if (dlo) {
//lwsl_notice("%s: dlo %p: adj y %d -> %d\n", __func__, dlo, dlo->box.y.whole, dlo->box.y.whole + delta.h.whole);
lws_fx_add(&dlo->box.y, &dlo->box.y, &delta.h);
}
} while (dlo);
/* go up parent chain until toplevel adjusting height of
* parent siblings below parent */
if (dp->flag_toplevel)
break;
dlo = dp;
//lwsl_notice("%s: dlo %p: adj h by %d\n", __func__, dlo, delta.h.whole);
lws_fx_add(&dlo->box.h, &dlo->box.h, &delta.h);
} while (1);
}
//#if defined(_DEBUG)
void
lws_display_dl_dump(lws_displaylist_t *dl)
{
lws_display_render_stack_t st[12]; /* DLO child stack */
int sp = 0;
lws_dll2_t *d = lws_dll2_get_head(&dl->dl);
#if (_LWS_ENABLED_LOGS & dlodump_loglevel)
static const char * const ind = " ";
#endif
char b[4][22], b1[4][22], dt[96];
if (!d) {
lwsl_notice("%s: empty dl\n", __func__);
return;
}
lwsl_notice("%s\n", __func__);
memset(&st, 0, sizeof(st));
st[0].dlo = lws_container_of(d, lws_dlo_t, list);
while (sp || st[0].dlo) {
lws_dlo_t *dlo = st[sp].dlo;
lws_box_t co;
//lws_fx_t t2;
if (!dlo) {
if (!sp) {
lwsl_err("%s: underflow\n", __func__);
return;
}
sp--;
continue;
}
lws_fx_add(&co.x, &st[sp].co.x, &dlo->box.x);
lws_fx_add(&co.y, &st[sp].co.y, &dlo->box.y);
co.w = dlo->box.w;
co.h = dlo->box.h;
lws_snprintf(dt, sizeof(dt), "rect: RGBA 0x%08X", (unsigned int)dlo->dc);
if (dlo->_destroy == lws_display_dlo_text_destroy) {
lws_dlo_text_t *text = lws_container_of(dlo, lws_dlo_text_t, dlo);
lws_snprintf(dt, sizeof(dt), "text: RGBA 0x%08X, chars: %u, %.*s",
(unsigned int)dlo->dc, (unsigned int)text->text_len,
(int)text->text_len, text->text ? text->text : "(empty)");
}
#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_UPNG) && defined(LWS_WITH_CLIENT)
else if (dlo->_destroy == lws_display_dlo_png_destroy)
lws_snprintf(dt, sizeof(dt), "png");
#endif
#if defined(LWS_WITH_NETWORK) && defined(LWS_WITH_JPEG) && defined(LWS_WITH_CLIENT)
else if (dlo->_destroy == lws_display_dlo_jpeg_destroy)
lws_snprintf(dt, sizeof(dt), "jpeg");
#endif
lws_fx_string(&dlo->box.x, b[0], sizeof(b[0]));
lws_fx_string(&dlo->box.y, b[1], sizeof(b[1]));
lws_fx_string(&dlo->box.w, b[2], sizeof(b[2]));
lws_fx_string(&dlo->box.h, b[3], sizeof(b[3]));
lws_fx_string(&co.x, b1[0], sizeof(b1[0]));
lws_fx_string(&co.y, b1[1], sizeof(b1[1]));
lws_fx_string(&co.w, b1[2], sizeof(b1[2]));
lws_fx_string(&co.h, b1[3], sizeof(b1[3]));
lwsl_dlodump("%.*s %p box: (%s, %s) [%s x %s], co: (%s, %s) [%s x %s], %s\n",
sp, ind, dlo, b[0], b[1], b[2], b[3],
b1[0], b1[1], b1[2], b1[3], dt);
d = dlo->list.next;
if (d)
st[sp].dlo = lws_container_of(d, lws_dlo_t, list);
else
st[sp].dlo = NULL;
/* go into any children */
if (dlo->children.head) {
if (sp + 1 == LWS_ARRAY_SIZE(st)) {
lwsl_err("%s: DLO stack overflow\n", __func__);
return;
}
st[++sp].dlo = lws_container_of(
dlo->children.head, lws_dlo_t, list);
st[sp].co = co;
}
}
}
//#endif
/*
* Go through every DLO once, setting its id->box to the final layout for the
* related dlo, if any
*/
lws_stateful_ret_t
lws_display_get_ids_boxes(lws_display_render_state_t *rs)
{
lws_dll2_t *d;
rs->lowest_id_y = 0;
d = lws_dll2_get_head(&rs->displaylist.dl);
if (!d)
/* nothing in dlo */
return LWS_SRET_OK;
memset(&rs->st[0].co, 0, sizeof(rs->st[0].co));
rs->st[0].dlo = lws_container_of(d, lws_dlo_t, list);
while (rs->sp || rs->st[0].dlo) {
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_box_t co;
lws_fx_t t2;
if (!dlo) {
rs->sp--;
continue;
}
lws_fx_add(&co.x, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&co.y, &rs->st[rs->sp].co.y, &dlo->box.y);
co.w = dlo->box.w;
co.h = dlo->box.h;
lws_fx_add(&t2, &co.y, &dlo->box.h);
if (dlo->id) {
lws_display_id_t *id = dlo->id;
lwsl_debug("%s: set id box %s\n", __func__, id->id);
id->box = co;
dlo->id = NULL; /* decouple us */
}
if (co.y.whole + co.h.whole > rs->lowest_id_y) {
rs->lowest_id_y = (lws_display_scalar)(co.y.whole + co.h.whole);
if (rs->lowest_id_y > rs->ic->wh_px[1].whole)
rs->lowest_id_y = (lws_display_scalar)rs->ic->wh_px[1].whole;
}
/* next sibling at this level if any */
d = dlo->list.next;
if (d)
rs->st[rs->sp].dlo = lws_container_of(d,
lws_dlo_t, list);
else
rs->st[rs->sp].dlo = NULL;
/* go into any children */
if (dlo->children.head) {
if (rs->sp + 1 == LWS_ARRAY_SIZE(rs->st)) {
lwsl_err("%s: DLO stack overflow\n",
__func__);
return LWS_SRET_FATAL;
}
rs->st[++rs->sp].dlo = lws_container_of(
dlo->children.head, lws_dlo_t, list);
rs->st[rs->sp].co = co;
continue;
}
}
lws_display_render_dump_ids(&rs->ids);
return LWS_SRET_OK;
}
lws_stateful_ret_t
lws_display_list_render_line(lws_display_render_state_t *rs)
{
lws_dll2_t *d;
if (rs->html == 1)
return LWS_SRET_WANT_INPUT;
if (!rs->sp && !rs->st[0].dlo) {
/* starting a line */
d = lws_dll2_get_head(&rs->displaylist.dl);
if (!d)
/* nothing in dlo */
return LWS_SRET_OK;
// memset(rs->line, 0, (size_t)rs->ic->wh_px[0].whole *
// (rs->ic->greyscale ? 1 : 3));
memset(&rs->st[0].co, 0, sizeof(rs->st[0].co));
rs->st[0].dlo = lws_container_of(d, lws_dlo_t, list);
}
while (rs->sp || rs->st[0].dlo) {
lws_dlo_t *dlo = rs->st[rs->sp].dlo;
lws_stateful_ret_t r;
lws_box_t co;
lws_fx_t t2;
if (!dlo) {
rs->sp--;
continue;
}
// lwsl_notice("%s: curr %d: %d %d %d %d\n", __func__, (int)rs->curr, (int)dlo->box.x.whole, (int)dlo->box.y.whole, (int)dlo->box.w.whole, (int)dlo->box.h.whole);
lws_fx_add(&co.x, &rs->st[rs->sp].co.x, &dlo->box.x);
lws_fx_add(&co.y, &rs->st[rs->sp].co.y, &dlo->box.y);
co.w = dlo->box.w;
co.h = dlo->box.h;
lws_fx_add(&t2, &co.y, &dlo->box.h);
if (rs->curr > lws_fx_roundup(&t2)) {
d = dlo->list.next;
rs->st[rs->sp].dlo = d ? lws_container_of(d, lws_dlo_t,
list) : NULL;
lws_display_dlo_destroy(&dlo);
continue;
}
#if 0
if (dlo->_destroy == lws_display_dlo_png_destroy)
lwsl_err("png line %d %d %d %d\n", rs->curr, co.y.whole - 1,
rs->st[rs->sp].co.y.whole, dlo->box.y.whole);
#endif
if (rs->curr >= co.y.whole - 1) {
r = dlo->render(rs);
//rs->ic, dlo, &rs->st[rs->sp].co,
// rs->curr, rs->line, &dlo->nle[0]);
if (r)
return r;
/* next sibling at this level if any */
d = dlo->list.next;
if (d)
rs->st[rs->sp].dlo = lws_container_of(d,
lws_dlo_t, list);
else
rs->st[rs->sp].dlo = NULL;
/* go into any children */
if (dlo->children.head) {
if (rs->sp + 1 == LWS_ARRAY_SIZE(rs->st)) {
lwsl_err("%s: DLO stack overflow\n",
__func__);
return LWS_SRET_FATAL;
}
rs->st[++rs->sp].dlo = lws_container_of(
dlo->children.head, lws_dlo_t, list);
rs->st[rs->sp].co = co;
continue;
}
} else {
/* next sibling at this level if any */
d = dlo->list.next;
if (d)
rs->st[rs->sp].dlo = lws_container_of(d,
lws_dlo_t, list);
else
rs->st[rs->sp].dlo = NULL;
}
}
return LWS_SRET_OK;
}
static int
dlo_clean_table_rows(lws_dll2_t *d, void *user)
{
lhp_table_row_t *r = lws_container_of(d, lhp_table_row_t, list);
lws_dll2_remove(d);
lws_free(r);
return 0;
}
static int
dlo_clean_table_cols(lws_dll2_t *d, void *user)
{
lhp_table_col_t *c = lws_container_of(d, lhp_table_col_t, list);
lws_dll2_remove(d);
lws_free(c);
return 0;
}
void
lws_display_dlo_destroy(lws_dlo_t **r)
{
if (!(*r))
return;
lws_dll2_remove(&(*r)->list);
lws_dll2_remove(&(*r)->col_list);
lws_dll2_remove(&(*r)->row_list);
while ((*r)->children.head) {
lws_dlo_t *d = lws_container_of((*r)->children.head,
lws_dlo_t, list);
lws_display_dlo_destroy(&d);
}
lws_dll2_foreach_safe(&(*r)->table_cols, NULL, dlo_clean_table_cols);
lws_dll2_foreach_safe(&(*r)->table_rows, NULL, dlo_clean_table_rows);
if ((*r)->_destroy)
(*r)->_destroy(*r);
lws_free_set_NULL(*r);
*r = NULL;
}
void
lws_display_list_destroy(lws_displaylist_t *dl)
{
if (!dl)
return;
while (dl->dl.head) {
lws_dlo_t *d = lws_container_of(dl->dl.head, lws_dlo_t, list);
lws_display_dlo_destroy(&d);
}
}
lws_dlo_filesystem_t *
lws_dlo_file_register(struct lws_context *cx, const lws_dlo_filesystem_t *f)
{
const lws_dlo_filesystem_t *b;
lws_dlo_filesystem_t *a;
b = lws_dlo_file_choose(cx, f->name);
if (b) {
lwsl_err("%s: dlo file %s already exists %p\n", __func__, b->name, b);
lws_dlo_file_unregister((lws_dlo_filesystem_t **)&b);
}
a = lws_malloc(sizeof(*a), __func__);
if (!a)
return NULL;
*a = *f;
lws_dll2_clear(&a->list);
lws_dll2_add_tail(&a->list, &cx->dlo_file);
lwsl_err("%s: dlo file %s registered at %p\n", __func__, a->name, a);
return a;
}
/*
* Only needed with heap-alloc'd lws_dlo_filesystem_t
*/
void
lws_dlo_file_unregister(lws_dlo_filesystem_t **f)
{
if (!*f)
return;
lws_dll2_remove(&(*f)->list);
lws_free_set_NULL(*f);
}
void
lws_dlo_file_unregister_by_name(struct lws_context *cx, const char *name)
{
lws_dlo_filesystem_t *a;
a = (lws_dlo_filesystem_t *)lws_dlo_file_choose(cx, name);
if (!a)
return;
lws_dll2_remove(&a->list);
lws_free_set_NULL(a);
}
static int
_lws_dlo_file_destroy(struct lws_dll2 *d, void *user)
{
lws_free(d);
return 0;
}
void
lws_dlo_file_destroy(struct lws_context *cx)
{
lws_dll2_foreach_safe(&cx->dlo_file, NULL, _lws_dlo_file_destroy);
}
const lws_dlo_filesystem_t *
lws_dlo_file_choose(struct lws_context *cx, const char *name)
{
lws_start_foreach_dll(struct lws_dll2 *, p,
lws_dll2_get_head(&cx->dlo_file)) {
const lws_dlo_filesystem_t *pn = lws_container_of(p,
lws_dlo_filesystem_t, list);
if (!strcmp(name, pn->name))
return pn;
} lws_end_foreach_dll(p);
return NULL;
}
static int
lws_display_id_destroy(struct lws_dll2 *d, void *user)
{
lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list);
lws_dll2_remove(&id->list);
lws_free(id);
return 0;
}
void
lws_display_render_free_ids(lws_display_render_state_t *rs)
{
lws_dll2_foreach_safe(&rs->ids, NULL, lws_display_id_destroy);
}
lws_display_id_t *
lws_display_render_get_id(lws_display_render_state_t *rs, const char *_id)
{
lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(&rs->ids)) {
lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list);
if (!strcmp(_id, id->id))
return id;
} lws_end_foreach_dll(d);
return NULL;
}
lws_display_id_t *
lws_display_render_add_id(lws_display_render_state_t *rs, const char *_id, void *priv)
{
lws_display_id_t *id;
id = lws_display_render_get_id(rs, _id);
if (id) {
id->priv_user = priv;
return id;
}
id = lws_zalloc(sizeof(*id), __func__);
if (id) {
lws_strncpy(id->id, _id, sizeof(id->id));
id->priv_user = priv;
lws_dll2_add_tail(&id->list, &rs->ids);
}
return id;
}
void
lws_display_render_dump_ids(lws_dll2_owner_t *ids)
{
lws_start_foreach_dll(struct lws_dll2 *, d, lws_dll2_get_head(ids)) {
lws_display_id_t *id = lws_container_of(d, lws_display_id_t, list);
if (!id->exists)
lwsl_notice(" id: '%s' (not present)\n", id->id);
else
lwsl_notice(" id: '%s', (%d,%d), %dx%d\n", id->id,
(int)id->box.x.whole, (int)id->box.y.whole,
(int)id->box.w.whole, (int)id->box.h.whole);
} lws_end_foreach_dll(d);
}
#if defined (LWS_WITH_FILE_OPS)
int
dlo_filesystem_fops_close(lws_fop_fd_t *fop_fd)
{
lws_free_set_NULL(*fop_fd);
return 0;
}
lws_fileofs_t
dlo_filesystem_fops_seek_cur(lws_fop_fd_t fop_fd,
lws_fileofs_t pos)
{
if (pos < 0)
fop_fd->pos = 0;
else
if (pos >= (long long)fop_fd->len)
fop_fd->pos = fop_fd->len;
else
fop_fd->pos = (lws_filepos_t)pos;
return (lws_fileofs_t)fop_fd->pos;
}
int
dlo_filesystem_fops_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
*amount = 0;
return -1;
}
int
dlo_filesystem_fops_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount,
uint8_t *buf, lws_filepos_t len)
{
const uint8_t *p = (uint8_t *)fop_fd->filesystem_priv;
lws_filepos_t amt = *amount;
*amount = 0;
if (fop_fd->len <= fop_fd->pos)
return 0;
if (amt > fop_fd->len - fop_fd->pos)
amt = fop_fd->len - fop_fd->pos;
if (amt > len)
amt = len;
memcpy(buf, p + fop_fd->pos, (size_t)amt);
fop_fd->pos += amt;
*amount = amt;
return 0;
}
lws_fop_fd_t
lws_dlo_filesystem_fops_open(const struct lws_plat_file_ops *fops_own,
const struct lws_plat_file_ops *fops,
const char *vfs_path, const char *vpath,
lws_fop_flags_t *flags)
{
const lws_dlo_filesystem_t *f = NULL;
lws_fop_fd_t fop_fd;
// lwsl_err("%s: %s\n", __func__, vpath);
f = lws_dlo_file_choose(fops->cx, vpath);
if (f) {
/* we will handle it then */
fop_fd = lws_zalloc(sizeof(*fop_fd), __func__);
if (!fop_fd)
return NULL;
fop_fd->fops = fops_own;
fop_fd->filesystem_priv = (void *)f->data;
fop_fd->pos = 0;
fop_fd->len = f->len;
// lwsl_notice("%s: Opened %s\n", __func__, vpath);
return fop_fd;
} else
lwsl_err("%s: failed to open %s\n", __func__, vpath);
return NULL;
}
const struct lws_plat_file_ops lws_dlo_fops = {
.LWS_FOP_OPEN = lws_dlo_filesystem_fops_open,
.LWS_FOP_CLOSE = dlo_filesystem_fops_close,
.LWS_FOP_SEEK_CUR = dlo_filesystem_fops_seek_cur,
.LWS_FOP_READ = dlo_filesystem_fops_read,
.LWS_FOP_WRITE = dlo_filesystem_fops_write,
.fi = { { "dlofs/", 6 } },
};
#endif

View File

@ -0,0 +1,57 @@
enum {
MCUFO_MAGIC = 0,
MCUFO_FLAGS_VER = 4,
MCUFO_FOFS_FULLNAME = 8,
MCUFO_FOFS_NAME = 0xc,
MCUFO_FOFS_DICT_DATA = 0x10,
MCUFO_SIZE_DICT_DATA = 0x14,
MCUFO_FOFS_DICT_OFS = 0x18,
MCUFO_COUNT_RLE_DICT = 0x1C,
MCUFO_COUNT_REF_RLE_DICT = 0x20,
MCUFO_FOFS_CHAR_RANGE_TABLES = 0x24,
MCUFO_COUNT_CHAR_RANGE_TABLES = 0x28,
MCUFO_UNICODE_FALLBACK = 0x2C,
MCUFO16_WIDTH = 0x30,
MCUFO16_HEIGHT = 0x32,
MCUFO16_MIN_X_ADV = 0x34,
MCUFO16_MAX_X_ADV = 0x36,
MCUFO16_BASELINE_X = 0x38,
MCUFO16_BASELINE_Y = 0x3a,
MCUFO16_LINE_HEIGHT = 0x3c,
};
void
dist_err_floyd_steinberg_grey(int n, int width, lws_greyscale_error_t *gedl_this,
lws_greyscale_error_t *gedl_next);
void
dist_err_floyd_steinberg_col(int n, int width, lws_colour_error_t *edl_this,
lws_colour_error_t *edl_next);
int
lws_display_alloc_diffusion(const lws_surface_info_t *ic, lws_surface_error_t **se);
size_t
utf8_bytes(uint8_t u);
int
lws_display_font_mcufont_getcwidth(lws_dlo_text_t *text, uint32_t unicode,
lws_fx_t *fx);
int
lws_display_dlo_text_attach_glyphs(lws_dlo_text_t *text);
lws_stateful_ret_t
lws_display_font_mcufont_render(struct lws_display_render_state *rs);
lws_font_glyph_t *
lws_display_font_mcufont_image_glyph(lws_dlo_text_t *text, uint32_t unicode,
char attach);
void
lws_lhp_ss_html_parse_from_lhp(lhp_ctx_t *lhp);
void
lws_lhp_image_dimensions_cb(lws_sorted_usec_list_t *sul);

View File

@ -0,0 +1,156 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* Mount and unmount overlayfs mountpoints (linux only)
*/
#include "private-lib-core.h"
#include <unistd.h>
#include <libmount/libmount.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
static int
rm_rf_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
{
char path[384];
if (!strcmp(lde->name, ".") || !strcmp(lde->name, ".."))
return 0;
lws_snprintf(path, sizeof(path), "%s/%s", dirpath, lde->name);
if (lde->type == LDOT_DIR) {
lws_dir(path, NULL, rm_rf_cb);
rmdir(path);
} else
unlink(path);
return 0;
}
int
lws_fsmount_mount(struct lws_fsmount *fsm)
{
struct libmnt_context *ctx;
char opts[512], c;
int n, m;
/*
* For robustness, there are a couple of sticky situations caused by
* previous mounts not cleaning up... 1) still mounted on the mountpoint
* and 2) junk in the session dir from the dead session.
*
* For 1), do a gratuitous umount attempts until it feels nothing to
* umount...
*/
c = fsm->mp[0];
while (!lws_fsmount_unmount(fsm))
fsm->mp[0] = c;
fsm->mp[0] = c;
/*
* ... for 2), generate the session dir basepath and destroy everything
* in it... it's less dangerous than it sounds because there are
* hardcoded unusual dir names in the base path, so it can't go wild
* even if the overlay path is empty or /
*/
lws_snprintf(opts, sizeof(opts), "%s/overlays/%s/session",
fsm->overlay_path, fsm->ovname);
lwsl_info("%s: emptying session dir %s\n", __func__, opts);
lws_dir(opts, NULL, rm_rf_cb);
/*
* Piece together the options for the overlay mount...
*/
n = lws_snprintf(opts, sizeof(opts), "lowerdir=");
for (m = LWS_ARRAY_SIZE(fsm->layers) - 1; m >= 0; m--)
if (fsm->layers[m]) {
if (n != 9)
opts[n++] = ':';
n += lws_snprintf(&opts[n], (size_t)(sizeof(opts) - (unsigned int)n),
"%s/%s/%s", fsm->layers_path,
fsm->distro, fsm->layers[m]);
}
n += lws_snprintf(&opts[n], (size_t)(sizeof(opts) - (unsigned int)n),
",upperdir=%s/overlays/%s/session",
fsm->overlay_path, fsm->ovname);
n += lws_snprintf(&opts[n], (size_t)(sizeof(opts) - (unsigned int)n),
",workdir=%s/overlays/%s/work",
fsm->overlay_path, fsm->ovname);
ctx = mnt_new_context();
if (!ctx)
return 1;
mnt_context_set_fstype(ctx, "overlay");
mnt_context_set_options(ctx, opts);
mnt_context_set_mflags(ctx, MS_NOATIME /* |MS_NOEXEC */);
mnt_context_set_target(ctx, fsm->mp);
mnt_context_set_source(ctx, "none");
lwsl_notice("%s: mount opts %s\n", __func__, opts);
puts(opts);
m = mnt_context_mount(ctx);
lwsl_notice("%s: mountpoint %s: %d\n", __func__, fsm->mp, m);
mnt_free_context(ctx);
return m;
}
int
lws_fsmount_unmount(struct lws_fsmount *fsm)
{
struct libmnt_context *ctx;
int m;
lwsl_notice("%s: %s\n", __func__, fsm->mp);
ctx = mnt_new_context();
if (!ctx)
return 1;
mnt_context_set_target(ctx, fsm->mp);
m = mnt_context_umount(ctx);
mnt_free_context(ctx);
fsm->mp[0] = '\0';
return m;
}

View File

@ -0,0 +1,315 @@
# LWS Full Text Search
## Introduction
![lwsac flow](/doc-assets/lws-fts.svg)
The general approach is to scan one or more UTF-8 input text "files" (they may
only exist in memory) and create an in-memory optimized trie for every token in
the file.
This can then be serialized out to disk in the form of a single index file (no
matter how many input files were involved or how large they were).
The implementation is designed to be modest on memory and cpu for both index
creation and querying, and suitable for weak machines with some kind of random
access storage. For searching only memory to hold results is required, the
actual searches and autocomplete suggestions are done very rapidly by seeking
around structures in the on-disk index file.
Function|Related Link
---|---
Public API|[include/libwebsockets/lws-fts.h](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-fts.h)
CI test app|[minimal-examples/api-tests/api-test-fts](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/api-tests/api-test-fts)
Demo minimal example|[minimal-examples/http-server/minimal-http-server-fulltext-search](https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/http-server/minimal-http-server-fulltext-search)
Live Demo|[https://libwebsockets.org/ftsdemo/](https://libwebsockets.org/ftsdemo/)
## Query API overview
Searching returns a potentially very large lwsac allocated object, with contents
and max size controlled by the members of a struct lws_fts_search_params passed
to the search function. Three kinds of result are possible:
### Autocomplete suggestions
These are useful to provide lists of extant results in
realtime as the user types characters that constrain the search. So if the
user has typed 'len', any hits for 'len' itself are reported along with
'length', and whatever else is in the index beginning 'len'.. The results are
selected using and are accompanied by an aggregated count of results down that
path, and the results so the "most likely" results already measured by potential
hits appear first.
These results are in a linked-list headed by `result.autocomplete_head` and
each is in a `struct lws_fts_result_autocomplete`.
They're enabled in the search results by giving the flag
`LWSFTS_F_QUERY_AUTOCOMPLETE` in the search parameter flags.
### Filepath results
Simply a list of input files containing the search term with some statistics,
one file is mentioned in a `struct lws_fts_result_filepath` result struct.
This would be useful for creating a selection UI to "drill down" to individual
files when there are many with matches.
This is enabled by the `LWSFTS_F_QUERY_FILES` search flag.
### Filepath and line results
Same as the file path list, but for each filepath, information on the line
numbers and input file offset where the line starts are provided.
This is enabled by `LWSFTS_F_QUERY_FILE_LINES`... if you additionally give
`LWSFTS_F_QUERY_QUOTE_LINE` flag then the contents of each hit line from the
input file are also provided.
## Result format inside the lwsac
A `struct lws_fts_result` at the start of the lwsac contains heads for linked-
lists of autocomplete and filepath results inside the lwsac.
For autocomplete suggestions, the string itself is immediately after the
`struct lws_fts_result_autocomplete` in memory. For filepath results, after
each `struct lws_fts_result_filepath` is
- match information depending on the flags given to the search
- the filepath string
You can always skip the line number table to get the filepath string by adding
.matches_length to the address of the byte after the struct.
The matches information is either
- 0 bytes per match
- 2x int32_t per match (8 bytes) if `LWSFTS_F_QUERY_FILE_LINES` given... the
first is the native-endian line number of the match, the second is the
byte offset in the original file where that line starts
- 2 x int32_t as above plus a const char * if `LWSFTS_F_QUERY_QUOTE_LINE` is
also given... this points to a NUL terminated string also stored in the
results lwsac that contains up to 255 chars of the line from the original
file. In some cases, the original file was either virtual (you are indexing
a git revision) or is not stored with the index, in that case you can't
usefully use `LWSFTS_F_QUERY_QUOTE_LINE`.
To facilitate interpreting what is stored per match, the original search flags
that created the result are stored in the `struct lws_fts_result`.
## Indexing In-memory and serialized to file
When creating the trie, in-memory structs are used with various optimization
schemes trading off memory usage for speed. While in-memory, it's possible to
add more indexed filepaths to the single index. Once the trie is complete in
terms of having indexed everything, it is serialized to disk.
These contain many additional housekeeping pointers and trie entries which can
be optimized out. Most in-memory values must be held literally in large types,
whereas most of the values in the serialized file use smaller VLI which use
more or less bytes according to the value. So the peak memory requirements for
large tries are much bigger than the size of the serialized trie file that is
output.
For the linux kernel at 4.14 and default indexing list on a 2.8GHz AMD
threadripper (using one thread), the stats are:
Name|Value
---|---
Files indexed|52932
Input corpus size|694MiB
Indexing cpu time|50.1s (>1000 files / sec; 13.8MBytes/sec)
Peak alloc|78MiB
Serialization time|202ms
Trie File size|347MiB
To index libwebsockets main branch under the same conditions:
Name|Value
---|---
Files indexed|489
Input corpus size|3MiB
Indexing time|123ms
Peak alloc|3MiB
Serialization time|1ms
Trie File size|1.4MiB
Once it's generated, querying the trie file is very inexpensive, even when there
are lots of results.
- trie entry child lists are kept sorted by the character they map to. This
allows discovering there is no match as soon as a character later in the
order than the one being matched is seen
- for the root trie, in addition to the linked-list child + sibling entries,
a 256-entry pointer table is associated with the root trie, allowing one-
step lookup. But as the table is 2KiB, it's too expensive to use on all
trie entries
## Structure on disk
All explicit multibyte numbers are stored in Network (MSB-first) byte order.
- file header
- filepath line number tables
- filepath information
- filepath map table
- tries, trie instances (hits), trie child tables
### VLI coding
VLI (Variable Length Integer) coding works like this
[b7 EON] [b6 .. b0 DATA]
If EON = 0, then DATA represents the Least-significant 7 bits of the number.
if EON = 1, DATA represents More-significant 7-bits that should be shifted
left until the byte with EON = 0 is found to terminate the number.
The VLI used is predicated around 32-bit unsigned integers
Examples:
- 0x30 = 48
- 0x81 30 = 176
- 0x81 0x80 0x00 = 16384
Bytes | Range
---|---
1|<= 127
2|<= 16K - 1
3|<= 2M -1
4|<= 256M - 1
5|<= 4G - 1
The coding is very efficient if there's a high probabilty the number being
stored is not large. So it's great for line numbers for example, where most
files have less that 16K lines and the VLI for the line number fits in 2 bytes,
but if you meet a huge file, the VLI coding can also handle it.
All numbers except a few in the headers that are actually written after the
following data are stored using VLI for space- efficiency without limiting
capability. The numbers that are fixed up after the fact have to have a fixed
size and can't use VLI.
### File header
The first byte of the file header where the magic is, is "fileoffset" 0. All
the stored "fileoffset"s are relative to that.
The header has a fixed size of 16 bytes.
size|function
---|---
32-bits|Magic 0xCA7A5F75
32-bits|Fileoffset to root trie entry
32-bits|Size of the trie file when it was created (to detect truncation)
32-bits|Fileoffset to the filepath map
32-bits|Number of filepaths
### Filepath line tables
Immediately after the file header are the line length tables.
As the input files are parsed, line length tables are written for each file...
at that time the rest of the parser data is held in memory so nothing else is
in the file yet. These allow you to map logical line numbers in the file to
file offsets space- and time- efficiently without having to walk through the
file contents.
The line information is cut into blocks, allowing quick skipping over the VLI
data that doesn't contain the line you want just by following the 8-byte header
part.
Once you find the block with your line, you have to iteratively add the VLIs
until you hit the one you want.
For normal text files with average line length below 128, the VLIs will
typically be a single byte. So a block of 200 line lengths is typically
208 bytes long.
There is a final linetable chunk consisting of all zeros to indicate the end
of the filepath line chunk series for a filepath.
size|function
---|---
16-bit|length of this chunk itself in bytes
16-bit|count of lines covered in this chunk
32-bit|count of bytes in the input file this chunk covers
VLI...|for each line in the chunk, the number of bytes in the line
### Filepaths
The single trie in the file may contain information from multiple files, for
example one trie may cover all files in a directory. The "Filepaths" are
listed after the line tables, and referred to by index thereafter.
For each filepath, one after the other:
size|function
---|---
VLI|fileoffset of the start of this filepath's line table
VLI|count of lines in the file
VLI|length of filepath in bytes
...|the filepath (with no NUL)
### Filepath map
To facilitate rapid filepath lookup, there's a filepath map table with a 32-bit
fileoffset per filepath. This is the way to convert filepath indexes to
information on the filepath like its name, etc
size|function
---|---
32-bit...|fileoffset to filepath table for each filepath
### Trie entries
Immediately after that, the trie entries are dumped, for each one a header:
#### Trie entry header
size|function
---|---
VLI|Fileoffset of first file table in this trie entry instance list
VLI|number of child trie entries this trie entry has
VLI|number of instances this trie entry has
The child list follows immediately after this header
#### Trie entry instance file
For each file that has instances of this symbol:
size|function
---|---
VLI|Fileoffset of next file table in this trie entry instance list
VLI|filepath index
VLI|count of line number instances following
#### Trie entry file line number table
Then for the file mentioned above, a list of all line numbers in the file with
the symbol in them, in ascending order. As a VLI, the median size per entry
will typically be ~15.9 bits due to the probability of line numbers below 16K.
size|function
---|---
VLI|line number
...
#### Trie entry child table
For each child node
size|function
---|---
VLI|file offset of child
VLI|instance count belonging directly to this child
VLI|aggregated number of instances down all descendent paths of child
VLI|aggregated number of children down all descendent paths of child
VLI|match string length
...|the match string

View File

@ -0,0 +1,23 @@
#include <libwebsockets.h>
/* if you need > 2GB trie files */
//typedef off_t jg2_file_offset;
typedef uint32_t jg2_file_offset;
struct lws_fts_file {
int fd;
jg2_file_offset root, flen, filepath_table;
int max_direct_hits;
int max_completion_hits;
int filepaths;
};
#define TRIE_FILE_HDR_SIZE 20
#define MAX_VLI 5
#define LWS_FTS_LINES_PER_CHUNK 200
int
rq32(unsigned char *b, uint32_t *d);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,276 @@
/*
* Copyright (c) 2000 - 2001 Kungliga Tekniska H<>gskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* originally downloaded from
*
* http://ftp.uninett.no/pub/OpenBSD/src/kerberosV/src/lib/roken/getifaddrs.c
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <libwebsockets.h>
#include "private-lib-core.h"
#ifdef LWS_HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif
#ifdef LWS_HAVE_NETINET_IN6_VAR_H
#include <netinet/in6_var.h>
#endif
#ifndef max
#define max(a, b) ((a) > (b) ? (a) : (b))
#endif
#include "getifaddrs.h"
static int
getifaddrs2(struct ifaddrs **ifap, int af, int siocgifconf, int siocgifflags,
size_t ifreq_sz)
{
int ret;
int fd;
size_t buf_size;
char *buf;
struct ifconf ifconf;
char *p;
size_t sz;
struct sockaddr sa_zero;
struct ifreq *ifr;
struct ifaddrs *start, **end = &start;
buf = NULL;
memset(&sa_zero, 0, sizeof(sa_zero));
fd = socket(af, SOCK_DGRAM, 0);
if (fd < 0)
return -1;
buf_size = 8192;
for (;;) {
buf = lws_zalloc(buf_size, "getifaddrs2");
if (buf == NULL) {
ret = ENOMEM;
goto error_out;
}
#if defined(__QNX__)
ifconf.ifc_len = (short)(int)buf_size;
#else
ifconf.ifc_len = (int)buf_size;
#endif
ifconf.ifc_buf = buf;
/*
* Solaris returns EINVAL when the buffer is too small.
*/
if (ioctl(fd, siocgifconf, &ifconf) < 0 && errno != EINVAL) {
ret = errno;
goto error_out;
}
/*
* Can the difference between a full and a overfull buf
* be determined?
*/
if (ifconf.ifc_len < (int)buf_size)
break;
lws_free(buf);
buf_size *= 2;
}
for (p = ifconf.ifc_buf; p < ifconf.ifc_buf + ifconf.ifc_len; p += sz) {
struct ifreq ifreq;
struct sockaddr *sa;
size_t salen;
ifr = (struct ifreq *)p;
sa = &ifr->ifr_addr;
sz = ifreq_sz;
salen = sizeof(struct sockaddr);
#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN
salen = sa->sa_len;
sz = max(sz, sizeof(ifr->ifr_name) + sa->sa_len);
#endif
#ifdef SA_LEN
salen = SA_LEN(sa);
sz = max(sz, sizeof(ifr->ifr_name) + SA_LEN(sa));
#endif
memset(&ifreq, 0, sizeof(ifreq));
memcpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name));
if (ioctl(fd, siocgifflags, &ifreq) < 0) {
ret = errno;
goto error_out;
}
*end = lws_malloc(sizeof(**end), "getifaddrs");
(*end)->ifa_next = NULL;
(*end)->ifa_name = strdup(ifr->ifr_name);
(*end)->ifa_flags = (unsigned int)ifreq.ifr_flags;
(*end)->ifa_addr = lws_malloc(salen, "getifaddrs");
memcpy((*end)->ifa_addr, sa, salen);
(*end)->ifa_netmask = NULL;
#if 0
/* fix these when we actually need them */
if (ifreq.ifr_flags & IFF_BROADCAST) {
(*end)->ifa_broadaddr =
lws_malloc(sizeof(ifr->ifr_broadaddr), "getifaddrs");
memcpy((*end)->ifa_broadaddr, &ifr->ifr_broadaddr,
sizeof(ifr->ifr_broadaddr));
} else if (ifreq.ifr_flags & IFF_POINTOPOINT) {
(*end)->ifa_dstaddr =
lws_malloc(sizeof(ifr->ifr_dstaddr), "getifaddrs");
memcpy((*end)->ifa_dstaddr, &ifr->ifr_dstaddr,
sizeof(ifr->ifr_dstaddr));
} else
(*end)->ifa_dstaddr = NULL;
#else
(*end)->ifa_dstaddr = NULL;
#endif
(*end)->ifa_data = NULL;
end = &(*end)->ifa_next;
}
*ifap = start;
close(fd);
lws_free(buf);
return 0;
error_out:
close(fd);
lws_free(buf);
errno = ret;
return -1;
}
int
getifaddrs(struct ifaddrs **ifap)
{
int ret = -1;
errno = ENXIO;
#if defined(AF_INET6) && defined(SIOCGIF6CONF) && defined(SIOCGIF6FLAGS)
if (ret)
ret = getifaddrs2(ifap, AF_INET6, SIOCGIF6CONF, SIOCGIF6FLAGS,
sizeof(struct in6_ifreq));
#endif
#if defined(LWS_HAVE_IPV6) && defined(SIOCGIFCONF)
if (ret)
ret = getifaddrs2(ifap, AF_INET6, SIOCGIFCONF, SIOCGIFFLAGS,
sizeof(struct ifreq));
#endif
#if defined(AF_INET) && defined(SIOCGIFCONF) && defined(SIOCGIFFLAGS)
if (ret)
ret = getifaddrs2(ifap, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
sizeof(struct ifreq));
#endif
return ret;
}
void
freeifaddrs(struct ifaddrs *ifp)
{
struct ifaddrs *p, *q;
for (p = ifp; p; ) {
lws_free(p->ifa_name);
lws_free(p->ifa_addr);
lws_free(p->ifa_dstaddr);
lws_free(p->ifa_netmask);
lws_free(p->ifa_data);
q = p;
p = p->ifa_next;
lws_free(q);
}
}
#ifdef TEST
void
print_addr(const char *s, struct sockaddr *sa)
{
int i;
printf(" %s=%d/", s, sa->sa_family);
#ifdef LWS_HAVE_STRUCT_SOCKADDR_SA_LEN
for (i = 0;
i < sa->sa_len - ((lws_intptr_t)sa->sa_data - (lws_intptr_t)&sa->sa_family); i++)
printf("%02x", ((unsigned char *)sa->sa_data)[i]);
#else
for (i = 0; i < sizeof(sa->sa_data); i++)
printf("%02x", ((unsigned char *)sa->sa_data)[i]);
#endif
printf("\n");
}
void
print_ifaddrs(struct ifaddrs *x)
{
struct ifaddrs *p;
for (p = x; p; p = p->ifa_next) {
printf("%s\n", p->ifa_name);
printf(" flags=%x\n", p->ifa_flags);
if (p->ifa_addr)
print_addr("addr", p->ifa_addr);
if (p->ifa_dstaddr)
print_addr("dstaddr", p->ifa_dstaddr);
if (p->ifa_netmask)
print_addr("netmask", p->ifa_netmask);
printf(" %p\n", p->ifa_data);
}
}
int
main()
{
struct ifaddrs *a = NULL, *b;
getifaddrs2(&a, AF_INET, SIOCGIFCONF, SIOCGIFFLAGS,
sizeof(struct ifreq));
print_ifaddrs(a);
printf("---\n");
getifaddrs(&b);
print_ifaddrs(b);
return 0;
}
#endif

View File

@ -0,0 +1,80 @@
#ifndef LWS_HAVE_GETIFADDRS
#define LWS_HAVE_GETIFADDRS 0
#endif
#if LWS_HAVE_GETIFADDRS
#include <sys/types.h>
#include <ifaddrs.h>
#else
#ifdef __cplusplus
extern "C" {
#endif
/*
* Copyright (c) 2000 Kungliga Tekniska H<>gskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* $KTH: ifaddrs.hin,v 1.3 2000/12/11 00:01:13 assar Exp $ */
#ifndef ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791
#define ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791
/*
* the interface is defined in terms of the fields below, and this is
* sometimes #define'd, so there seems to be no simple way of solving
* this and this seemed the best. */
#undef ifa_dstaddr
struct ifaddrs {
struct ifaddrs *ifa_next;
char *ifa_name;
unsigned int ifa_flags;
struct sockaddr *ifa_addr;
struct sockaddr *ifa_netmask;
struct sockaddr *ifa_dstaddr;
void *ifa_data;
};
#ifndef ifa_broadaddr
#define ifa_broadaddr ifa_dstaddr
#endif
int getifaddrs(struct ifaddrs **);
void freeifaddrs(struct ifaddrs *);
#endif /* __ifaddrs_h__ */
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,228 @@
/******************************************************************************
*
* Filename: ieeehalfprecision.c
* Programmer: James Tursa
* Version: 1.0
* Date: March 3, 2009
* Copyright: (c) 2009 by James Tursa, All Rights Reserved
*
* This code uses the BSD License:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* This file contains C code to convert between IEEE double, single, and half
* precision floating point formats. The intended use is for standalone C code
* that does not rely on MATLAB mex.h. The bit pattern for the half precision
* floating point format is stored in a 16-bit unsigned int variable. The half
* precision bit pattern definition is:
*
* 1 bit sign bit
* 5 bits exponent, biased by 15
* 10 bits mantissa, hidden leading bit, normalized to 1.0
*
* Special floating point bit patterns recognized and supported:
*
* All exponent bits zero:
* - If all mantissa bits are zero, then number is zero (possibly signed)
* - Otherwise, number is a denormalized bit pattern
*
* All exponent bits set to 1:
* - If all mantissa bits are zero, then number is +Infinity or -Infinity
* - Otherwise, number is NaN (Not a Number)
*
* For the denormalized cases, note that 2^(-24) is the smallest number that can
* be represented in half precision exactly. 2^(-25) will convert to 2^(-24)
* because of the rounding algorithm used, and 2^(-26) is too small and
* underflows to zero.
*
******************************************************************************/
/*
changes by K. Rogovin:
- changed macros UINT16_TYPE, etc to types from stdint.h
(i.e. UINT16_TYPE-->uint16_t, INT16_TYPE-->int16_t, etc)
- removed double conversion routines.
- changed run time checks of endianness to compile time macro.
- removed return value from routines
- changed source parameter type from * to const *
- changed pointer types from void ot uint16_t and uint32_t
*/
/*
* andy@warmcat.com:
*
* - clean style and indenting
* - convert to single operation
* - export as lws_
*/
#include <string.h>
#include <stdint.h>
void
lws_singles2halfp(uint16_t *hp, uint32_t x)
{
uint32_t xs, xe, xm;
uint16_t hs, he, hm;
int hes;
if (!(x & 0x7FFFFFFFu)) {
/* Signed zero */
*hp = (uint16_t)(x >> 16);
return;
}
xs = x & 0x80000000u; // Pick off sign bit
xe = x & 0x7F800000u; // Pick off exponent bits
xm = x & 0x007FFFFFu; // Pick off mantissa bits
if (xe == 0) { // Denormal will underflow, return a signed zero
*hp = (uint16_t) (xs >> 16);
return;
}
if (xe == 0x7F800000u) { // Inf or NaN (all the exponent bits are set)
if (!xm) { // If mantissa is zero ...
*hp = (uint16_t) ((xs >> 16) | 0x7C00u); // Signed Inf
return;
}
*hp = (uint16_t) 0xFE00u; // NaN, only 1st mantissa bit set
return;
}
/* Normalized number */
hs = (uint16_t) (xs >> 16); // Sign bit
/* Exponent unbias the single, then bias the halfp */
hes = ((int)(xe >> 23)) - 127 + 15;
if (hes >= 0x1F) { // Overflow
*hp = (uint16_t) ((xs >> 16) | 0x7C00u); // Signed Inf
return;
}
if (hes <= 0) { // Underflow
if ((14 - hes) > 24)
/*
* Mantissa shifted all the way off & no
* rounding possibility
*/
hm = (uint16_t) 0u; // Set mantissa to zero
else {
xm |= 0x00800000u; // Add the hidden leading bit
hm = (uint16_t) (xm >> (14 - hes)); // Mantissa
if ((xm >> (13 - hes)) & 1u) // Check for rounding
/* Round, might overflow into exp bit,
* but this is OK */
hm = (uint16_t)(hm + 1u);
}
/* Combine sign bit and mantissa bits, biased exponent is 0 */
*hp = hs | hm;
return;
}
he = (uint16_t)(hes << 10); // Exponent
hm = (uint16_t)(xm >> 13); // Mantissa
if (xm & 0x00001000u) // Check for rounding
/* Round, might overflow to inf, this is OK */
*hp = (uint16_t)((hs | he | hm) + (uint16_t)1u);
else
*hp = hs | he | hm; // No rounding
}
void
lws_halfp2singles(uint32_t *xp, uint16_t h)
{
uint16_t hs, he, hm;
uint32_t xs, xe, xm;
int32_t xes;
int e;
if (!(h & 0x7FFFu)) { // Signed zero
*xp = ((uint32_t)h) << 16; // Return the signed zero
return;
}
hs = h & 0x8000u; // Pick off sign bit
he = h & 0x7C00u; // Pick off exponent bits
hm = h & 0x03FFu; // Pick off mantissa bits
if (!he) { // Denormal will convert to normalized
e = -1;
/* figure out how much extra to adjust the exponent */
do {
e++;
hm = (uint16_t)(hm << 1);
/* Shift until leading bit overflows into exponent */
} while (!(hm & 0x0400u));
xs = ((uint32_t) hs) << 16; // Sign bit
/* Exponent unbias the halfp, then bias the single */
xes = ((int32_t)(he >> 10)) - 15 + 127 - e;
xe = (uint32_t)(xes << 23); // Exponent
xm = ((uint32_t)(hm & 0x03FFu)) << 13; // Mantissa
*xp = xs | xe | xm;
return;
}
if (he == 0x7C00u) { /* Inf or NaN (all the exponent bits are set) */
if (!hm) { /* If mantissa is zero ...
* Signed Inf
*/
*xp = (((uint32_t)hs) << 16) | ((uint32_t)0x7F800000u);
return;
}
/* ... NaN, only 1st mantissa bit set */
*xp = (uint32_t)0xFFC00000u;
return;
}
/* Normalized number */
xs = ((uint32_t)hs) << 16; // Sign bit
/* Exponent unbias the halfp, then bias the single */
xes = ((int32_t)(he >> 10)) - 15 + 127;
xe = (uint32_t)(xes << 23); // Exponent
xm = ((uint32_t)hm) << 13; // Mantissa
/* Combine sign bit, exponent bits, and mantissa bits */
*xp = xs | xe | xm;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,386 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* We use the lejp parse stack to replace the callback context for JSON
* subtrees.
*
* It's optionally done when we see we're in a [] batch of reqs, we pass each
* unitary req to the internal req parser.
*
* Each req does it to hand off the parsing of the parameters section.
*/
#include <private-lib-core.h>
#include "private-lib-misc-jrpc.h"
static const char * const paths[] = {
"jsonrpc",
"method",
"version",
"params",
"id",
/* only for responses --> */
"result",
"error",
"code",
"message",
"data",
};
enum enum_paths {
LEJPN_JSONRPC,
LEJPN_METHOD,
LEJPN_VERSION,
LEJPN_PARAMS,
LEJPN_ID,
/* only for responses --> */
LEJPN_RESULT,
LEJPN_ERROR,
LEJPN_E_CODE,
LEJPN_E_MESSAGE,
LEJPN_E_DATA,
};
/*
* Get the registered handler for a method name... a registered handler for
* a NULL method name matches any other unmatched name.
*/
static const lws_jrpc_method_t *
lws_jrpc_method_lookup(lws_jrpc_t *jrpc, const char *method_name)
{
const lws_jrpc_method_t *m = jrpc->methods, *m_null = NULL;
while (1) {
if (!m->method_name)
return m;
if (!strcmp(method_name, m->method_name))
return m;
m++;
}
return m_null;
}
static signed char
req_cb(struct lejp_ctx *ctx, char reason)
{
lws_jrpc_obj_t *r = (lws_jrpc_obj_t *)ctx->user;
lws_jrpc_t *jrpc;
char *p;
lwsl_warn("%s: %d '%s' %s (sp %d, pst_sp %d)\n", __func__, reason, ctx->path, ctx->buf, ctx->sp, ctx->pst_sp);
if (reason == LEJPCB_PAIR_NAME && ctx->path_match - 1 == LEJPN_PARAMS) {
if (r->response)
goto fail_invalid_members;
/*
* Params are a wormhole to another LEJP parser context to deal
* with, chosen based on the method name and the callbacks
* associated with that at init time.
*
* Params may be provided in a toplevel array, called a "batch",
* these are treated as n independent subrequests to be handled
* sequentially, and if the request is parseable, the scope of
* errors is only the current batch entry.
*/
jrpc = lws_container_of(r->list.owner, lws_jrpc_t, req_owner);
r->pmethod = lws_jrpc_method_lookup(jrpc, r->method);
if (!r->pmethod || !r->pmethod->cb)
/*
* There's nothing we can do with no method binding, or
* one that lacks a callback...
*/
goto fail_method_not_found;
r->inside_params = 1;
lwsl_notice("%s: params: entering subparser\n", __func__);
lejp_parser_push(ctx, r, r->pmethod->paths,
(uint8_t)r->pmethod->count_paths, r->pmethod->cb);
}
if (reason == LEJPCB_COMPLETE && !r->response) {
if (!r->has_jrpc_member)
goto fail_invalid_request;
if (r->method[0] && !r->pmethod) {
jrpc = lws_container_of(r->list.owner, lws_jrpc_t,
req_owner);
r->pmethod = lws_jrpc_method_lookup(jrpc, r->method);
if (!r->pmethod || !r->pmethod->cb)
/*
* There's nothing we can do with no method
* binding, or one that lacks a callback...
*/
goto fail_method_not_found;
}
/*
* Indicate that the whole of the request has been parsed now
* and the id is known, so the method can complete and finalize
* its response
*/
r->pmethod->cb(ctx, LEJPCB_USER_START);
return 0;
}
/* we only match on the prepared path strings */
if (!(reason & LEJP_FLAG_CB_IS_VALUE) || !ctx->path_match)
return 0;
if (ctx->path_match - 1 >= LEJPN_RESULT && !r->response)
goto fail_invalid_members;
switch (ctx->path_match - 1) {
case LEJPN_JSONRPC:
/*
* A String specifying the version of the JSON-RPC protocol.
* MUST be exactly "2.0".
*/
if (ctx->npos != 3 && strcmp(ctx->buf, "2.0")) {
r->parse_result = LWSJRPCWKE__INVALID_REQUEST;
return -1;
}
r->has_jrpc_member = 1;
break;
case LEJPN_METHOD:
if (r->response)
goto fail_invalid_members;
/*
* Method is defined to be a string... anything else is invalid
*/
if (reason != LEJPCB_VAL_STR_END)
goto fail_invalid_request;
/*
* Restrict the method length to something sane
*/
if (ctx->npos > sizeof(r->method) - 1)
goto fail_method_not_found;
lws_strnncpy(r->method, ctx->buf, ctx->npos, sizeof(r->method));
/* defer trying to use it so we catch parser errors */
break;
case LEJPN_ID:
/*
* "An identifier established by the Client that MUST contain a
* String, Number, or NULL value if included. If it is not
* included it is assumed to be a notification. The value SHOULD
* normally not be Null and Numbers SHOULD NOT contain
* fractional parts."
*
* We defaulted the id to null, let's continue to store the id
* exactly as it would be reissued, ie, if a string, then we'll
* add the quotes around it now.
*
* Restrict the method length and type to something sane
*/
if (ctx->npos > sizeof(r->id) - 3 ||
reason == LEJPCB_VAL_TRUE ||
reason == LEJPCB_VAL_FALSE ||
/* if float, has "fractional part" */
reason == LEJPCB_VAL_NUM_FLOAT)
goto fail_invalid_request;
r->seen_id = 1;
if (reason == LEJPCB_VAL_NULL)
/* it already defaults to null */
break;
p = r->id;
if (reason == LEJPCB_VAL_STR_END)
*p++ = '\"';
lws_strnncpy(p, ctx->buf, ctx->npos, sizeof(r->id) - 2);
if (reason == LEJPCB_VAL_STR_END) {
p += strlen(p);
*p++ = '\"';
*p = '\0';
}
break;
case LEJPN_VERSION:
/*
* Restrict the method length to something sane
*/
if (ctx->npos > sizeof(r->version) - 1)
goto fail_invalid_request;
lws_strnncpy(r->version, ctx->buf, ctx->npos, sizeof(r->version));
break;
/*
* Only for responses
*/
case LEJPN_RESULT:
break;
case LEJPN_ERROR:
break;
case LEJPN_E_CODE:
break;
case LEJPN_E_MESSAGE:
break;
case LEJPN_E_DATA:
break;
}
return 0;
fail_invalid_members:
r->parse_result = LWSJRPCE__INVALID_MEMBERS;
return -1;
fail_invalid_request:
r->parse_result = LWSJRPCWKE__INVALID_REQUEST;
return -1;
fail_method_not_found:
r->parse_result = LWSJRPCWKE__METHOD_NOT_FOUND;
return -1;
}
const char *
lws_jrpc_obj_id(const struct lws_jrpc_obj *r)
{
return r->id;
}
/*
* Return code is >= 0 if completed, representing the amount of unused data in
* the input buffer. -1 indicates more input data needed, <-1 indicates an
* error from the LWSJRPCWKE_ set above
*/
int
lws_jrpc_obj_parse(lws_jrpc_t *jrpc, int type, void *opaque,
const char *buf, size_t l, lws_jrpc_obj_t **_r)
{
lws_jrpc_obj_t *r = *_r;
int n;
if (!r) {
/*
* We need to init the request object
*/
r = *_r = malloc(sizeof(*r));
if (!r)
return LEJP_REJECT_UNKNOWN; /* OOM */
memset(r, 0, sizeof *r);
lws_dll2_add_tail(&r->list, &jrpc->req_owner);
r->opaque = opaque;
r->response = type == LWSJRPC_PARSE_RESPONSE;
lws_strncpy(r->id, "null", sizeof(r->id));
lejp_construct(&r->lejp_ctx, req_cb, r, paths,
LWS_ARRAY_SIZE(paths));
}
n = lejp_parse(&r->lejp_ctx, (uint8_t *)buf, (int)l);
lwsl_debug("%s: raw parse result %d\n", __func__, n);
if (n == LEJP_REJECT_CALLBACK)
return r->parse_result;
if (n < -1)
return LWSJRPCWKE__PARSE_ERROR;
return n;
}
void *
lws_jrpc_obj_get_opaque(const struct lws_jrpc_obj * r)
{
return (void *)r->opaque;
}
void
lws_jrpc_obj_destroy(lws_jrpc_obj_t **_r)
{
lws_jrpc_obj_t *r = *_r;
if (!r)
return;
lws_dll2_remove(&r->list);
free(r);
*_r = NULL;
}
struct lws_jrpc *
lws_jrpc_create(const lws_jrpc_method_t *methods, void *opaque)
{
lws_jrpc_t *j = malloc(sizeof(*j));
if (!j)
return NULL;
memset(j, 0, sizeof(*j));
j->opaque = opaque;
j->methods = methods;
return j;
}
void *
lws_jrpc_get_opaque(const struct lws_jrpc *jrpc)
{
return (void *)jrpc->opaque;
}
void
lws_jrpc_destroy(lws_jrpc_t **_jrpc)
{
struct lws_jrpc *jrpc = *_jrpc;
if (!jrpc)
return;
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
jrpc->req_owner.head) {
lws_jrpc_obj_t *r = lws_container_of(p, lws_jrpc_obj_t, list);
lws_jrpc_obj_destroy(&r);
} lws_end_foreach_dll_safe(p, p1);
free(jrpc);
*_jrpc = NULL;
}

View File

@ -0,0 +1,88 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This written from scratch, but props to falk-werner for his earlier
* work on top of lws for JRPC.
*
* https://github.com/falk-werner/jrpc
*
* https://www.jsonrpc.org/specification
*
* LWS JRPC takes the approach to stream-parse the incoming JRPC object in
* place to maximize the flexibility and parameter sizes that can be handled.
* Although "id" is often last, actually it has no users except to append the
* same id to the response.
*
* Therefore we parse the outer JSON and treat params as a wormhole to be
* parsed by a method-bound user callback.
*
* Streamed request processing must buffer its output before sending, since
* it does not know until the end if it must replace the intended response
* with an exception. It may not know that it wants to make an exception
* until it really processes all the params either. Results must be held in
* a side buffer until the response is able to complete or has errored.
*
* Types for id, method and params are ill-defined. They're all treated as
* strings internally, so a "method": 1 is handled as the string "1". id
* may be NULL, if so it's explicitly returned in the response with "id":null
* Whether id came in as a non-quoted number is remembered and is reproduced
* when giving the id.
*/
/*
* Opaque object representing a request both at the sender and receiver
*/
typedef struct lws_jrpc_obj {
lws_dll2_t list;
struct lejp_ctx lejp_ctx;
void *opaque;
const lws_jrpc_method_t *pmethod; /* only look up once if multi part */
char id[16]; /* includes quotes if was string */
char method[48];
/*
* Eg Sony API "getCurrentExternalTerminalsStatus" (30 chars)
* https://developer.sony.com/develop/audio-control-api/api-references/api-overview-2
*/
char version[4]; /* Eg for Sony, "2.0" */
int parse_result;
uint8_t count_batch_objects;
uint8_t seen_id :1;
uint8_t inside_params :1;
uint8_t has_jrpc_member :1;
uint8_t response :1;
} lws_jrpc_obj_t;
typedef struct lws_jrpc {
lws_dll2_owner_t req_owner;
const lws_jrpc_method_t *methods;
void *opaque;
} lws_jrpc_t;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,950 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include <string.h>
#include <stdio.h>
static const char * const parser_errs[] = {
"",
"",
"No opening '{'",
"Expected closing '}'",
"Expected '\"'",
"String underrun",
"Illegal unescaped control char",
"Illegal escape format",
"Illegal hex number",
"Expected ':'",
"Illegal value start",
"Digit required after decimal point",
"Bad number format",
"Bad exponent format",
"Unknown token",
"Too many ']'",
"Mismatched ']'",
"Expected ']'",
"JSON nesting limit exceeded",
"Nesting tracking used up",
"Number too long",
"Comma or block end expected",
"Unknown",
"Parser callback errored (see earlier error)",
};
/**
* lejp_construct - prepare a struct lejp_ctx for use
*
* \param ctx: pointer to your struct lejp_ctx
* \param callback: your user callback which will received parsed tokens
* \param user: optional user data pointer untouched by lejp
* \param paths: your array of name elements you are interested in
* \param count_paths: LWS_ARRAY_SIZE() of @paths
*
* Prepares your context struct for use with lejp
*/
void
lejp_construct(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason), void *user,
const char * const *paths, unsigned char count_paths)
{
ctx->st[0].s = 0;
ctx->st[0].p = 0;
ctx->st[0].i = 0;
ctx->st[0].b = 0;
ctx->sp = 0;
ctx->ipos = 0;
ctx->outer_array = 0;
ctx->path_match = 0;
ctx->path_stride = 0;
ctx->path[0] = '\0';
ctx->user = user;
ctx->line = 1;
ctx->flags = 0; /* user may set after construction */
ctx->pst_sp = 0;
ctx->pst[0].callback = callback;
ctx->pst[0].paths = paths;
ctx->pst[0].count_paths = count_paths;
ctx->pst[0].user = NULL;
ctx->pst[0].ppos = 0;
ctx->pst[0].callback(ctx, LEJPCB_CONSTRUCTED);
}
/**
* lejp_destruct - retire a previously constructed struct lejp_ctx
*
* \param ctx: pointer to your struct lejp_ctx
*
* lejp does not perform any allocations, but since your user code might, this
* provides a one-time LEJPCB_DESTRUCTED callback at destruction time where
* you can clean up in your callback.
*/
void
lejp_destruct(struct lejp_ctx *ctx)
{
/* no allocations... just let callback know what it happening */
if (ctx && ctx->pst[0].callback)
ctx->pst[0].callback(ctx, LEJPCB_DESTRUCTED);
}
/**
* lejp_change_callback - switch to a different callback from now on
*
* \param ctx: pointer to your struct lejp_ctx
* \param callback: your user callback which will received parsed tokens
*
* This tells the old callback it was destroyed, in case you want to take any
* action because that callback "lost focus", then changes to the new
* callback and tells it first that it was constructed, and then started.
*
* Changing callback is a cheap and powerful trick to split out handlers
* according to information earlier in the parse. For example you may have
* a JSON pair "schema" whose value defines what can be expected for the rest
* of the JSON. Rather than having one huge callback for all cases, you can
* have an initial one looking for "schema" which then calls
* lejp_change_callback() to a handler specific for the schema.
*
* Notice that afterwards, you need to construct the context again anyway to
* parse another JSON object, and the callback is reset then to the main,
* schema-interpreting one. The construction action is very lightweight.
*/
void
lejp_change_callback(struct lejp_ctx *ctx,
signed char (*callback)(struct lejp_ctx *ctx, char reason))
{
ctx->pst[0].callback(ctx, LEJPCB_DESTRUCTED);
ctx->pst[0].callback = callback;
ctx->pst[0].callback(ctx, LEJPCB_CONSTRUCTED);
ctx->pst[0].callback(ctx, LEJPCB_START);
}
void
lejp_check_path_match(struct lejp_ctx *ctx)
{
const char *p, *q;
int n;
size_t s = sizeof(char *);
if (ctx->path_stride)
s = ctx->path_stride;
/* we only need to check if a match is not active */
for (n = 0; //!ctx->path_match &&
n < ctx->pst[ctx->pst_sp].count_paths; n++) {
ctx->wildcount = 0;
p = ctx->path;
q = *((char **)(((char *)ctx->pst[ctx->pst_sp].paths) + ((unsigned int)n * s)));
//lwsl_notice("%s: %s %s\n", __func__, p, q);
while (*p && *q) {
if (*q != '*') {
if (*p != *q)
break;
p++;
q++;
continue;
}
ctx->wild[ctx->wildcount++] = (uint16_t)lws_ptr_diff_size_t(p, ctx->path);
q++;
/*
* if * has something after it, match to .
* if ends with *, eat everything.
* This implies match sequences must be ordered like
* x.*.*
* x.*
* if both options are possible
*/
while (*p && ((*p != '.' && *p != '[') || !*q))
p++;
}
if (*p || *q)
continue;
ctx->path_match = (uint8_t)(n + 1);
ctx->path_match_len = ctx->pst[ctx->pst_sp].ppos;
return;
}
if (!ctx->path_match)
ctx->wildcount = 0;
}
int
lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len)
{
int n;
if (wildcard >= ctx->wildcount || !len)
return 0;
n = ctx->wild[wildcard];
while (--len && n < ctx->pst[ctx->pst_sp].ppos &&
(n == ctx->wild[wildcard] || ctx->path[n] != '.'))
*dest++ = ctx->path[n++];
*dest = '\0';
n++;
return n - ctx->wild[wildcard];
}
/**
* lejp_parse - interpret some more incoming data incrementally
*
* \param ctx: previously constructed parsing context
* \param json: char buffer with the new data to interpret
* \param len: amount of data in the buffer
*
* Because lejp is a stream parser, it incrementally parses as new data
* becomes available, maintaining all state in the context struct. So an
* incomplete JSON is a normal situation, getting you a LEJP_CONTINUE
* return, signalling there's no error but to call again with more data when
* it comes to complete the parsing. Successful parsing completes with a
* 0 or positive integer indicating how much of the last input buffer was
* unused.
*/
static const char esc_char[] = "\"\\/bfnrt";
static const char esc_tran[] = "\"\\/\b\f\n\r\t";
static const char tokens[] = "rue alse ull ";
int
lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len)
{
unsigned char c, n, s, defer = 0;
int ret = LEJP_REJECT_UNKNOWN;
if (!ctx->sp && !ctx->pst[ctx->pst_sp].ppos)
ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_START);
while (len--) {
c = *json++;
s = (unsigned char)ctx->st[ctx->sp].s;
/* skip whitespace unless we should care */
if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '#') {
if (c == '\n') {
ctx->line++;
ctx->st[ctx->sp].s &= (char)~LEJP_FLAG_WS_COMMENTLINE;
}
if (!(s & LEJP_FLAG_WS_KEEP)) {
if (c == '#')
ctx->st[ctx->sp].s |=
LEJP_FLAG_WS_COMMENTLINE;
continue;
}
}
if (ctx->st[ctx->sp].s & LEJP_FLAG_WS_COMMENTLINE)
continue;
switch (s) {
case LEJP_IDLE:
if (!ctx->sp && c == '[') {
/* push */
ctx->outer_array = 1;
ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END;
c = LEJP_MP_VALUE;
ctx->path[ctx->pst[ctx->pst_sp].ppos++] = '[';
ctx->path[ctx->pst[ctx->pst_sp].ppos++] = ']';
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
if (ctx->flags & LEJP_FLAG_FEAT_LEADING_WC)
lejp_check_path_match(ctx);
if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_START))
goto reject_callback;
ctx->i[ctx->ipos++] = 0;
if (ctx->flags & LEJP_FLAG_FEAT_LEADING_WC)
lejp_check_path_match(ctx);
if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) {
ret = LEJP_REJECT_MP_DELIM_ISTACK;
goto reject;
}
goto add_stack_level;
}
if (c != '{') {
ret = LEJP_REJECT_IDLE_NO_BRACE;
goto reject;
}
if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES) {
/* since insides of {} can have ',', we should
* add an index level so we can count them
*/
ctx->i[ctx->ipos++] = 0;
if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) {
ret = LEJP_REJECT_MP_DELIM_ISTACK;
goto reject;
}
}
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_OBJECT_START))
goto reject_callback;
ctx->st[ctx->sp].s = LEJP_MEMBERS;
break;
case LEJP_MEMBERS:
if (c == '}') {
if (ctx->sp >= 1)
goto pop_level;
ctx->st[ctx->sp].s = LEJP_IDLE;
ret = LEJP_REJECT_MEMBERS_NO_CLOSE;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_M_P;
goto redo_character;
case LEJP_M_P:
if (c != '\"') {
ret = LEJP_REJECT_MP_NO_OPEN_QUOTE;
goto reject;
}
/* push */
ctx->st[ctx->sp].s = LEJP_MP_DELIM;
c = LEJP_MP_STRING;
goto add_stack_level;
case LEJP_MP_STRING:
if (c == '\"') {
if (!ctx->sp) { /* JSON can't end on quote */
ret = LEJP_REJECT_MP_STRING_UNDERRUN;
goto reject;
}
if (ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) {
ctx->buf[ctx->npos] = '\0';
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_VAL_STR_END) < 0)
goto reject_callback;
}
/* pop */
ctx->sp--;
break;
}
if (c == '\\') {
ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC;
break;
}
if (c < ' ') {/* "control characters" not allowed */
ret = LEJP_REJECT_MP_ILLEGAL_CTRL;
goto reject;
}
goto emit_string_char;
case LEJP_MP_STRING_ESC:
if (c == 'u') {
ctx->st[ctx->sp].s = LEJP_MP_STRING_ESC_U1;
ctx->uni = 0;
break;
}
for (n = 0; n < sizeof(esc_char); n++) {
if (c != esc_char[n])
continue;
/* found it */
c = (unsigned char)esc_tran[n];
ctx->st[ctx->sp].s = LEJP_MP_STRING;
goto emit_string_char;
}
ret = LEJP_REJECT_MP_STRING_ESC_ILLEGAL_ESC;
/* illegal escape char */
goto reject;
case LEJP_MP_STRING_ESC_U1:
case LEJP_MP_STRING_ESC_U2:
case LEJP_MP_STRING_ESC_U3:
case LEJP_MP_STRING_ESC_U4:
ctx->uni = (uint16_t)(ctx->uni << 4);
if (c >= '0' && c <= '9')
ctx->uni |= (uint16_t)(c - '0');
else
if (c >= 'a' && c <= 'f')
ctx->uni |= (uint16_t)(c - 'a' + 10);
else
if (c >= 'A' && c <= 'F')
ctx->uni |= (uint16_t)(c - 'A' + 10);
else {
ret = LEJP_REJECT_ILLEGAL_HEX;
goto reject;
}
ctx->st[ctx->sp].s++;
switch (s) {
case LEJP_MP_STRING_ESC_U2:
if (ctx->uni < 0x08)
break;
/*
* 0x08-0xff (0x0800 - 0xffff)
* emit 3-byte UTF-8
*/
c = (unsigned char)(0xe0 | ((ctx->uni >> 4) & 0xf));
goto emit_string_char;
case LEJP_MP_STRING_ESC_U3:
if (ctx->uni >= 0x080) {
/*
* 0x080 - 0xfff (0x0800 - 0xffff)
* middle 3-byte seq
* send ....XXXXXX..
*/
c = (unsigned char)(0x80 | ((ctx->uni >> 2) & 0x3f));
goto emit_string_char;
}
if (ctx->uni < 0x008)
break;
/*
* 0x008 - 0x7f (0x0080 - 0x07ff)
* start 2-byte seq
*/
c = (unsigned char)(0xc0 | (ctx->uni >> 2));
goto emit_string_char;
case LEJP_MP_STRING_ESC_U4:
if (ctx->uni >= 0x0080)
/* end of 2 or 3-byte seq */
c = (unsigned char)(0x80 | (ctx->uni & 0x3f));
else
/* literal */
c = (unsigned char)ctx->uni;
ctx->st[ctx->sp].s = LEJP_MP_STRING;
goto emit_string_char;
default:
break;
}
break;
case LEJP_MP_DELIM:
if (c != ':') {
ret = LEJP_REJECT_MP_DELIM_MISSING_COLON;
goto reject;
}
ctx->st[ctx->sp].s = LEJP_MP_VALUE;
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
lejp_check_path_match(ctx);
if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_PAIR_NAME))
goto reject_callback;
break;
case LEJP_MP_VALUE:
if (c == '-' || (c >= '0' && c <= '9')) {
ctx->npos = 0;
ctx->dcount = 0;
ctx->f = 0;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT;
goto redo_character;
}
switch (c) {
case'\"':
/* push */
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
c = LEJP_MP_STRING;
ctx->npos = 0;
ctx->buf[0] = '\0';
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_VAL_STR_START))
goto reject_callback;
goto add_stack_level;
case '{':
/* push */
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
c = LEJP_MEMBERS;
lejp_check_path_match(ctx);
if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES) {
/* since insides of {} can have ',', we should
* add an index level so we can count them
*/
ctx->i[ctx->ipos++] = 0;
if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) {
ret = LEJP_REJECT_MP_DELIM_ISTACK;
goto reject;
}
}
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_OBJECT_START))
goto reject_callback;
ctx->path_match = 0;
goto add_stack_level;
case '[':
/* push */
ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END;
c = LEJP_MP_VALUE;
if (ctx->pst[ctx->pst_sp].ppos + 3u >=
sizeof(ctx->path))
goto reject;
ctx->path[ctx->pst[ctx->pst_sp].ppos++] = '[';
ctx->path[ctx->pst[ctx->pst_sp].ppos++] = ']';
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
if (ctx->flags & LEJP_FLAG_FEAT_LEADING_WC)
lejp_check_path_match(ctx);
if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_START))
goto reject_callback;
if (ctx->flags & LEJP_FLAG_FEAT_LEADING_WC)
lejp_check_path_match(ctx);
ctx->i[ctx->ipos++] = 0;
if (ctx->ipos > LWS_ARRAY_SIZE(ctx->i)) {
ret = LEJP_REJECT_MP_DELIM_ISTACK;
goto reject;
}
goto add_stack_level;
case ']':
/* pop */
if (!ctx->sp) { /* JSON can't end on ] */
ret = LEJP_REJECT_MP_C_OR_E_UNDERF;
goto reject;
}
ctx->sp--;
if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) {
ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY;
goto reject;
}
/* drop the path [n] bit */
if (ctx->sp) {
ctx->pst[ctx->pst_sp].ppos = (unsigned char)
ctx->st[ctx->sp - 1].p;
ctx->ipos = (unsigned char)ctx->st[ctx->sp - 1].i;
} else
if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES)
ctx->ipos--;
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
if (ctx->path_match &&
ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
if (ctx->pst_sp && !ctx->sp)
lejp_parser_pop(ctx);
if (ctx->flags & LEJP_FLAG_FEAT_LEADING_WC)
lejp_check_path_match(ctx);
if (ctx->outer_array && !ctx->sp) { /* ended on ] */
n = LEJPCB_ARRAY_END;
goto completed;
}
goto array_end;
case 't': /* true */
ctx->uni = 0;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
break;
case 'f':
ctx->uni = 4;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
break;
case 'n':
ctx->uni = 4 + 5;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_TOK;
break;
default:
ret = LEJP_REJECT_MP_DELIM_BAD_VALUE_START;
goto reject;
}
break;
case LEJP_MP_VALUE_NUM_INT:
if (!ctx->npos && c == '-') {
ctx->f |= LEJP_SEEN_MINUS;
goto append_npos;
}
if (ctx->dcount < 20 && c >= '0' && c <= '9') {
if (ctx->f & LEJP_SEEN_POINT)
ctx->f |= LEJP_SEEN_POST_POINT;
ctx->dcount++;
goto append_npos;
}
if (c == '.') {
if (!ctx->dcount || (ctx->f & LEJP_SEEN_POINT)) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
ctx->f |= LEJP_SEEN_POINT;
goto append_npos;
}
/*
* before exponent, if we had . we must have had at
* least one more digit
*/
if ((ctx->f &
(LEJP_SEEN_POINT | LEJP_SEEN_POST_POINT)) ==
LEJP_SEEN_POINT) {
ret = LEJP_REJECT_MP_VAL_NUM_INT_NO_FRAC;
goto reject;
}
if (c == 'e' || c == 'E') {
if (ctx->f & LEJP_SEEN_EXP) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
ctx->f |= LEJP_SEEN_EXP;
ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_EXP;
goto append_npos;
}
/* if none of the above, did we even have a number? */
if (!ctx->dcount) {
ret = LEJP_REJECT_MP_VAL_NUM_FORMAT;
goto reject;
}
ctx->buf[ctx->npos] = '\0';
if (ctx->f & LEJP_SEEN_POINT) {
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_VAL_NUM_FLOAT))
goto reject_callback;
} else {
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_VAL_NUM_INT))
goto reject_callback;
}
/* then this is the post-number character, loop */
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
goto redo_character;
case LEJP_MP_VALUE_NUM_EXP:
ctx->st[ctx->sp].s = LEJP_MP_VALUE_NUM_INT;
if (c >= '0' && c <= '9')
goto redo_character;
if (c == '+' || c == '-')
goto append_npos;
ret = LEJP_REJECT_MP_VAL_NUM_EXP_BAD_EXP;
goto reject;
case LEJP_MP_VALUE_TOK: /* true, false, null */
if (c != tokens[ctx->uni]) {
ret = LEJP_REJECT_MP_VAL_TOK_UNKNOWN;
goto reject;
}
ctx->uni++;
if (tokens[ctx->uni] != ' ')
break;
switch (ctx->uni) {
case 3:
ctx->buf[0] = '1';
ctx->buf[1] = '\0';
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_VAL_TRUE))
goto reject_callback;
break;
case 8:
ctx->buf[0] = '0';
ctx->buf[1] = '\0';
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_VAL_FALSE))
goto reject_callback;
break;
case 12:
ctx->buf[0] = '\0';
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_VAL_NULL))
goto reject_callback;
break;
}
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
break;
case LEJP_MP_COMMA_OR_END:
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
if (c == ',') {
/* increment this stack level's index */
ctx->st[ctx->sp].s = LEJP_M_P;
if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES)
if (ctx->ipos)
ctx->i[ctx->ipos - 1]++;
if (!ctx->sp) {
ctx->pst[ctx->pst_sp].ppos = 0;
/*
* since we came back to root level,
* no path can still match
*/
ctx->path_match = 0;
break;
}
ctx->pst[ctx->pst_sp].ppos = (unsigned char)ctx->st[ctx->sp - 1].p;
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
if (ctx->path_match &&
ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
lejp_check_path_match(ctx);
if (ctx->st[ctx->sp - 1].s != LEJP_MP_ARRAY_END)
break;
/* top level is definitely an array... */
if (!(ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES))
if (ctx->ipos)
ctx->i[ctx->ipos - 1]++;
ctx->st[ctx->sp].s = LEJP_MP_VALUE;
break;
}
if (c == ']') {
if (!ctx->sp) {
ret = LEJP_REJECT_MP_C_OR_E_UNDERF;
goto reject;
}
/* pop */
ctx->sp--;
if (ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END) {
ret = LEJP_REJECT_MP_C_OR_E_NOTARRAY;
goto reject;
}
/* drop the path [n] bit */
if (ctx->sp) {
ctx->pst[ctx->pst_sp].ppos = (unsigned char)
ctx->st[ctx->sp - 1].p;
ctx->ipos = (unsigned char)ctx->st[ctx->sp - 1].i;
} else
if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES)
ctx->ipos--;
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
if (ctx->path_match &&
ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
if (ctx->outer_array && !ctx->sp) { /* ended on ] */
n = LEJPCB_ARRAY_END;
goto completed;
}
if (ctx->pst_sp && !ctx->sp)
defer = 1;
/* do LEJP_MP_ARRAY_END processing */
goto redo_character;
}
if (c != '}') {
ret = LEJP_REJECT_MP_C_OR_E_NEITHER;
goto reject;
}
if (!ctx->sp) {
n = LEJPCB_OBJECT_END;
completed:
ctx->path_match = 0;
//lejp_check_path_match(ctx);
if (ctx->pst[ctx->pst_sp].callback(ctx, (char)n) ||
ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_COMPLETE))
goto reject_callback;
/* done, return unused amount */
return len;
}
/* pop */
pop_level:
ctx->sp--;
if (ctx->sp) {
ctx->pst[ctx->pst_sp].ppos = (unsigned char)ctx->st[ctx->sp].p;
ctx->ipos = (unsigned char)ctx->st[ctx->sp].i;
} else
if (ctx->flags & LEJP_FLAG_FEAT_OBJECT_INDEXES)
ctx->ipos--;
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
if (ctx->path_match &&
ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len)
/*
* we shrank the path to be
* smaller than the matching point
*/
ctx->path_match = 0;
lejp_check_path_match(ctx);
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_OBJECT_END))
goto reject_callback;
if (ctx->pst_sp && !ctx->sp)
lejp_parser_pop(ctx);
break;
case LEJP_MP_ARRAY_END:
array_end:
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
if (c == ',') {
/* increment this stack level's index */
if (ctx->ipos)
ctx->i[ctx->ipos - 1]++;
ctx->st[ctx->sp].s = LEJP_MP_VALUE;
if (ctx->sp)
ctx->pst[ctx->pst_sp].ppos = (unsigned char)
ctx->st[ctx->sp - 1].p;
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
lejp_check_path_match(ctx);
break;
}
if (c != ']') {
ret = LEJP_REJECT_MP_ARRAY_END_MISSING;
goto reject;
}
lejp_check_path_match(ctx);
ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END;
ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_END);
if (defer) {
lejp_parser_pop(ctx);
defer = 0;
}
break;
}
continue;
emit_string_char:
if (!ctx->sp || ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) {
/* assemble the string value into chunks */
ctx->buf[ctx->npos++] = (char)c;
if (ctx->npos == sizeof(ctx->buf) - 1) {
if (ctx->pst[ctx->pst_sp].callback(ctx,
LEJPCB_VAL_STR_CHUNK))
goto reject_callback;
ctx->npos = 0;
}
continue;
}
/* name part of name:value pair */
ctx->path[ctx->pst[ctx->pst_sp].ppos++] = (char)c;
continue;
add_stack_level:
/* push on to the object stack */
if (ctx->pst[ctx->pst_sp].ppos &&
ctx->st[ctx->sp].s != LEJP_MP_COMMA_OR_END &&
ctx->st[ctx->sp].s != LEJP_MP_ARRAY_END)
ctx->path[ctx->pst[ctx->pst_sp].ppos++] = '.';
ctx->st[ctx->sp].p = (char)ctx->pst[ctx->pst_sp].ppos;
ctx->st[ctx->sp].i = (char)ctx->ipos;
if (++ctx->sp == LWS_ARRAY_SIZE(ctx->st)) {
ret = LEJP_REJECT_STACK_OVERFLOW;
goto reject;
}
ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0';
ctx->st[ctx->sp].s = (char)c;
ctx->st[ctx->sp].b = 0;
continue;
append_npos:
if (ctx->npos >= sizeof(ctx->buf)) {
ret = LEJP_REJECT_NUM_TOO_LONG;
goto reject;
}
ctx->buf[ctx->npos++] = (char)c;
continue;
redo_character:
json--;
len++;
}
return LEJP_CONTINUE;
reject_callback:
ret = LEJP_REJECT_CALLBACK;
reject:
ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_FAILED);
return ret;
}
int
lejp_parser_push(struct lejp_ctx *ctx, void *user, const char * const *paths,
unsigned char paths_count, lejp_callback lejp_cb)
{
struct _lejp_parsing_stack *p;
if (ctx->pst_sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH)
return -1;
lejp_check_path_match(ctx);
ctx->pst[ctx->pst_sp].path_match = ctx->path_match;
ctx->pst_sp++;
p = &ctx->pst[ctx->pst_sp];
p->user = user;
p->callback = lejp_cb;
p->paths = paths;
p->count_paths = paths_count;
p->ppos = 0;
ctx->path_match = 0;
lejp_check_path_match(ctx);
lwsl_debug("%s: pushed parser stack to %d (path %s)\n", __func__,
ctx->pst_sp, ctx->path);
return 0;
}
int
lejp_parser_pop(struct lejp_ctx *ctx)
{
if (!ctx->pst_sp)
return -1;
ctx->pst_sp--;
lwsl_debug("%s: popped parser stack to %d\n", __func__, ctx->pst_sp);
ctx->path_match = 0; /* force it to check */
lejp_check_path_match(ctx);
return 0;
}
const char *
lejp_error_to_string(int e)
{
if (e > 0)
e = 0;
else
e = -e;
if (e >= (int)LWS_ARRAY_SIZE(parser_errs))
return "Unknown error";
return parser_errs[e];
}

View File

@ -0,0 +1,241 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* SS bindings for html5 parser
*/
#include <private-lib-core.h>
LWS_SS_USER_TYPEDEF
lws_flow_t flow;
lhp_ctx_t lhp; /* html ss owns html parser */
lws_dl_rend_t drt;
lws_sorted_usec_list_t sul;
lws_display_render_state_t *rs;
struct lws_context *cx;
} htmlss_t;
static void
lws_lhp_ss_html_parse(lws_sorted_usec_list_t *sul)
{
htmlss_t *m = lws_container_of(sul, htmlss_t, sul);
lws_stateful_ret_t r;
size_t zero = 0;
do {
if (lws_flow_feed(&m->flow)) {
lwsl_notice("%s: returning from flow_feed\n", __func__);
return;
}
// lwsl_notice("%s: html_parse in len %d\n", __func__, (int)m->flow.len);
/* creates display list objects from html */
r = lws_lhp_parse(&m->lhp, (const uint8_t **)&m->flow.data,
(size_t *)&m->flow.len);
lws_flow_req(&m->flow);
if ((r & LWS_SRET_WANT_INPUT) && !m->flow.len && !m->lhp.await_css_done) {
if (m->flow.state == LWSDLOFLOW_STATE_READ) {
lwsl_warn("%s: returning to await more input\n", __func__);
return;
}
lwsl_warn("%s: inferring we are finished\n", __func__);
break;
}
if (r & LWS_SRET_AWAIT_RETRY) {
if (!m->lhp.await_css_done)
lws_sul_schedule(m->cx, 0, &m->sul, lws_lhp_ss_html_parse, 1);
return;
}
if (r & (LWS_SRET_NO_FURTHER_OUT | LWS_SRET_FATAL)) {
lwsl_warn("%s: r 0x%x\n", __func__, r);
break;
}
} while (1);
/* Finalize the html parse and clean up */
lwsl_notice("%s: DESTROYING the lhp\n", __func__);
m->lhp.flags = LHP_FLAG_DOCUMENT_END;
lws_lhp_parse(&m->lhp, (const uint8_t **)NULL, &zero);
lws_lhp_destruct(&m->lhp);
m->rs->html = 2; /* html completed.. rs outlives the html ss and priv */
lws_display_dl_dump(m->drt.dl);
/* schedule starting the render */
lws_sul_schedule(m->cx, 0, &m->rs->sul, m->lhp.ssevcb, 1);
lws_ss_destroy(&m->ss);
}
void
lws_lhp_ss_html_parse_from_lhp(lhp_ctx_t *lhp)
{
htmlss_t *m = lws_container_of(lhp, htmlss_t, lhp);
lws_lhp_ss_html_parse(&m->sul);
}
/* secure streams payload interface */
static lws_ss_state_return_t
htmlss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
{
htmlss_t *m = (htmlss_t *)userobj;
lws_ss_state_return_t r = LWSSSSRET_OK;
if (len &&
lws_buflist_append_segment(&m->flow.bl, buf, len) < 0)
return LWSSSSRET_DISCONNECT_ME;
lwsl_notice("%s: buflen size %d\n", __func__,
(int)lws_buflist_total_len(&m->flow.bl));
if (flags & LWSSS_FLAG_EOM) {
m->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED;
r = LWSSSSRET_DISCONNECT_ME;
}
lws_sul_schedule(m->cx, 0, &m->sul, lws_lhp_ss_html_parse, 1);
return r;
}
static lws_ss_state_return_t
htmlss_state(void *userobj, void *sh, lws_ss_constate_t state,
lws_ss_tx_ordinal_t ack)
{
htmlss_t *m = (htmlss_t *)userobj;
switch (state) {
case LWSSSCS_CREATING:
break;
case LWSSSCS_DISCONNECTED:
m->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED;
m->flow.h = NULL;
break;
case LWSSSCS_DESTROYING:
lws_lhp_destruct(&m->lhp);
lws_buflist_destroy_all_segments(&m->flow.bl);
m->drt.dl = NULL;
break;
default:
break;
}
return LWSSSSRET_OK;
}
static LWS_SS_INFO("__default", htmlss_t)
.rx = htmlss_rx,
.state = htmlss_state,
.manual_initial_tx_credit = 1024
};
/* prep rs->displaylist, rs->ic */
int
lws_lhp_ss_browse(struct lws_context *cx, lws_display_render_state_t *rs,
const char *url, sul_cb_t render)
{
struct lws_ss_handle *h = NULL;
lws_ss_info_t ssi;
int32_t w = 64 * 1024;
htmlss_t *m;
/* fetch via SS */
#if defined(LWS_PLAT_BAREMETAL) || defined(LWS_PLAT_FREERTOS)
w = 4096;
#endif
ssi = ssi_htmlss_t;
ssi.manual_initial_tx_credit = w;
if (lws_ss_create(cx, 0, &ssi, NULL, &h, NULL, NULL)) {
lwsl_err("%s: ss create failed\n", __func__);
return 1; /* failed */
}
m = (htmlss_t *)lws_ss_to_user_object(h);
m->cx = cx;
m->flow.h = h;
m->flow.window = w;
m->drt.dl = &rs->displaylist;
m->drt.w = rs->ic->wh_px[0].whole;
m->drt.h = rs->ic->wh_px[1].whole;
m->rs = rs;
m->rs->html = 1; /* render must wait for html to complete */
if (lws_lhp_construct(&m->lhp, lhp_displaylist_layout, &m->drt, rs->ic)) {
lwsl_err("%s: lhp create %s failed\n", __func__, url);
goto bail1;
}
m->lhp.user1 = cx;
m->lhp.base_url = strdup(url);
m->lhp.ssevcb = render;
m->lhp.ssevsul = &rs->sul;
m->lhp.sshtmlevcb = lws_lhp_ss_html_parse;
m->lhp.sshtmlevsul = &m->sul;
m->lhp.ids = &rs->ids;
if (lws_ss_set_metadata(m->ss, "endpoint", url, strlen(url))) {
lwsl_err("%s: failed to use metadata %s\n", __func__, url);
goto bail2;
}
if (lws_ss_set_metadata(m->ss, "ua", "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:95.0) Gecko/20100101 Firefox/95.0", 76)) {
lwsl_err("%s: failed to use metadata ua\n", __func__);
goto bail2;
}
if (lws_ss_set_metadata(m->ss, "acc", "text/html,image/jpeg,image/png,", 30)) {
lwsl_err("%s: failed to use metadata ua\n", __func__);
goto bail2;
}
if (lws_ss_client_connect(m->ss))
goto bail2;
return 0;
bail2:
lws_lhp_destruct(&m->lhp);
bail1:
lws_ss_destroy(&h);
return 1;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,298 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
struct lws_ring *
lws_ring_create(size_t element_len, size_t count,
void (*destroy_element)(void *))
{
struct lws_ring *ring = lws_malloc(sizeof(*ring), "ring create");
if (!ring)
return NULL;
ring->buflen = (uint32_t)(count * element_len);
ring->element_len = (uint32_t)element_len;
ring->head = 0;
ring->oldest_tail = 0;
ring->destroy_element = destroy_element;
ring->buf = lws_malloc(ring->buflen, "ring buf");
if (!ring->buf) {
lws_free(ring);
return NULL;
}
return ring;
}
void
lws_ring_destroy(struct lws_ring *ring)
{
if (ring->destroy_element)
while (ring->oldest_tail != ring->head) {
ring->destroy_element((uint8_t *)ring->buf +
ring->oldest_tail);
ring->oldest_tail =
(ring->oldest_tail + ring->element_len) %
ring->buflen;
}
if (ring->buf)
lws_free_set_NULL(ring->buf);
lws_free(ring);
}
size_t
lws_ring_get_count_free_elements(struct lws_ring *ring)
{
int f;
/*
* possible ringbuf patterns
*
* h == t
* |--------t***h---|
* |**h-----------t*|
* |t**************h|
* |*****ht*********|
*/
if (ring->head == ring->oldest_tail)
f = (int)(ring->buflen - ring->element_len);
else
if (ring->head < ring->oldest_tail)
f = (int)((ring->oldest_tail - ring->head) -
ring->element_len);
else
f = (int)((ring->buflen - ring->head) + ring->oldest_tail -
ring->element_len);
if (f < 2)
return 0;
return (unsigned int)f / ring->element_len;
}
size_t
lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail)
{ int f;
if (!tail)
tail = &ring->oldest_tail;
/*
* possible ringbuf patterns
*
* h == t
* |--------t***h---|
* |**h-----------t*|
* |t**************h|
* |*****ht*********|
*/
if (ring->head == *tail)
f = 0;
else
if (ring->head > *tail)
f = (int)(ring->head - *tail);
else
f = (int)((ring->buflen - *tail) + ring->head);
return (unsigned int)f / ring->element_len;
}
int
lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start,
size_t *bytes)
{
int n;
/* n is how many bytes the whole fifo can take */
n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len);
if (!n)
return 1;
if (ring->head + (unsigned int)n > ring->buflen) {
*start = (void *)(((uint8_t *)ring->buf) + ring->head);
*bytes = ring->buflen - ring->head;
return 0;
}
*start = (void *)(((uint8_t *)ring->buf) + ring->head);
*bytes = (unsigned int)n;
return 0;
}
void
lws_ring_bump_head(struct lws_ring *ring, size_t bytes)
{
ring->head = (ring->head + (uint32_t)bytes) % ring->buflen;
}
size_t
lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count)
{
const uint8_t *osrc = src;
size_t m;
int n;
/* n is how many bytes the whole fifo can take */
n = (int)(lws_ring_get_count_free_elements(ring) * ring->element_len);
/* restrict n to how much we want to insert */
if ((uint32_t)n > max_count * ring->element_len)
n = (int)(max_count * ring->element_len);
/*
* n is legal to insert, but as an optimization we can cut the
* insert into one or two memcpys, depending on if it wraps
*/
if (ring->head + (unsigned int)n > ring->buflen) {
/*
* He does wrap. The first memcpy should take us up to
* the end of the buffer
*/
m = ring->buflen - ring->head;
memcpy(((uint8_t *)ring->buf) + ring->head, src, m);
/* we know it will wrap exactly back to zero */
ring->head = 0;
/* adapt the second memcpy for what we already did */
src = ((uint8_t *)src) + m;
n = n - (int)m;
}
memcpy(((uint8_t *)ring->buf) + ring->head, src, (size_t)n);
ring->head = (ring->head + (unsigned int)n) % ring->buflen;
return (unsigned long)(((uint8_t *)src + (unsigned int)n) - osrc) / ring->element_len;
}
size_t
lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest,
size_t max_count)
{
uint8_t *odest = dest;
void *orig_tail = tail;
uint32_t fake_tail;
int m, n;
if (!tail) {
fake_tail = ring->oldest_tail;
tail = &fake_tail;
}
/* n is how many bytes the whole fifo has for us */
n = (int)(lws_ring_get_count_waiting_elements(ring, tail) *
ring->element_len);
/* restrict n to how much we want to insert */
if ((size_t)n > max_count * ring->element_len)
n = (int)(max_count * ring->element_len);
if (!dest) {
*tail = ((*tail) + (unsigned int)n) % ring->buflen;
if (!orig_tail) /* single tail */
lws_ring_update_oldest_tail(ring, *tail);
return (unsigned int)n / ring->element_len;
}
if (*tail + (unsigned int)n > ring->buflen) {
/*
* He does wrap. The first memcpy should take us up to
* the end of the buffer
*/
m = (int32_t)(ring->buflen - *tail);
memcpy(dest, ((uint8_t *)ring->buf) + *tail, (size_t)m);
/* we know it will wrap exactly back to zero */
*tail = 0;
/* adapt the second memcpy for what we already did */
dest = ((uint8_t *)dest) + m;
n -= m;
}
memcpy(dest, ((uint8_t *)ring->buf) + *tail, (size_t)n);
*tail = ((*tail) + (unsigned int)n) % ring->buflen;
if (!orig_tail) /* single tail */
lws_ring_update_oldest_tail(ring, *tail);
return (unsigned int)(((uint8_t *)dest + n) - odest) / (unsigned int)ring->element_len;
}
const void *
lws_ring_get_element(struct lws_ring *ring, uint32_t *tail)
{
if (!tail)
tail = &ring->oldest_tail;
if (*tail == ring->head)
return NULL;
return ((uint8_t *)ring->buf) + *tail;
}
void
lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail)
{
if (!ring->destroy_element) {
ring->oldest_tail = tail;
return;
}
while (ring->oldest_tail != tail) {
ring->destroy_element((uint8_t *)ring->buf + ring->oldest_tail);
ring->oldest_tail = (ring->oldest_tail + ring->element_len) %
ring->buflen;
}
}
uint32_t
lws_ring_get_oldest_tail(struct lws_ring *ring)
{
return ring->oldest_tail;
}
void
lws_ring_dump(struct lws_ring *ring, uint32_t *tail)
{
if (tail == NULL)
tail = &ring->oldest_tail;
lwsl_notice("ring %p: buflen %u, elem_len %u, head %u, oldest_tail %u\n"
" free_elems: %u; for tail %u, waiting elements: %u\n",
ring, (int)ring->buflen, (int)ring->element_len,
(int)ring->head, (int)ring->oldest_tail,
(int)lws_ring_get_count_free_elements(ring), (int)*tail,
(int)lws_ring_get_count_waiting_elements(ring, tail));
}

View File

@ -0,0 +1,895 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <libwebsockets.h>
#include <private-lib-core.h>
#include <assert.h>
signed char
lws_struct_schema_only_lejp_cb(struct lejp_ctx *ctx, char reason)
{
lws_struct_args_t *a = (lws_struct_args_t *)ctx->user;
const lws_struct_map_t *map = a->map_st[ctx->pst_sp];
size_t n = a->map_entries_st[ctx->pst_sp], imp = 0;
lejp_callback cb = map->lejp_cb;
if (reason == LEJPCB_PAIR_NAME && strcmp(ctx->path, "schema")) {
/*
* If not "schema", the schema is implicit rather than
* explicitly given, ie, he just goes ahead and starts using
* member names that imply a particular type. For example, he
* may have an implicit type normally, and a different one for
* exceptions that just starts using "error-message" or whatever
* and we can understand that's the exception type now.
*
* Let's look into each of the maps in the top level array
* and match the first one that mentions the name he gave here,
* and bind to the associated type / create a toplevel object
* of that type.
*/
while (n--) {
int m, child_members = (int)map->child_map_size;
for (m = 0; m < child_members; m++) {
const lws_struct_map_t *child = &map->child_map[m];
if (!strcmp(ctx->path, child->colname)) {
/*
* We matched on him... map is pointing
* to the right toplevel type, let's
* just pick up from there as if we
* matched the explicit schema name...
*/
ctx->path_match = 1;
imp = 1;
goto matched;
}
}
map++;
}
lwsl_notice("%s: can't match implicit schema %s\n",
__func__, ctx->path);
return -1;
}
if (reason != LEJPCB_VAL_STR_END || ctx->path_match != 1)
return 0;
/* If "schema", then look for a matching name in the map array */
while (n--) {
if (strcmp(ctx->buf, map->colname)) {
map++;
continue;
}
matched:
a->dest = lwsac_use_zero(&a->ac, map->aux, a->ac_block_size);
if (!a->dest) {
lwsl_err("%s: OOT\n", __func__);
return 1;
}
a->dest_len = map->aux;
if (!ctx->pst_sp)
a->top_schema_index = (int)(map - a->map_st[ctx->pst_sp]);
if (!cb)
cb = lws_struct_default_lejp_cb;
lejp_parser_push(ctx, a->dest, &map->child_map[0].colname,
(uint8_t)map->child_map_size, cb);
a->map_st[ctx->pst_sp] = map->child_map;
a->map_entries_st[ctx->pst_sp] = map->child_map_size;
// lwsl_notice("%s: child map ofs_clist %d\n", __func__,
// (int)a->map_st[ctx->pst_sp]->ofs_clist);
if (imp)
return cb(ctx, reason);
return 0;
}
lwsl_notice("%s: unknown schema %s\n", __func__, ctx->buf);
return 1;
}
static int
lws_struct_lejp_push(struct lejp_ctx *ctx, lws_struct_args_t *args,
const lws_struct_map_t *map, uint8_t *ch)
{
lejp_callback cb = map->lejp_cb;
if (!cb)
cb = lws_struct_default_lejp_cb;
lejp_parser_push(ctx, ch, (const char * const*)map->child_map,
(uint8_t)map->child_map_size, cb);
args->map_st[ctx->pst_sp] = map->child_map;
args->map_entries_st[ctx->pst_sp] = map->child_map_size;
return 0;
}
signed char
lws_struct_default_lejp_cb(struct lejp_ctx *ctx, char reason)
{
lws_struct_args_t *args = (lws_struct_args_t *)ctx->user;
const lws_struct_map_t *map, *pmap = NULL;
uint8_t *ch;
size_t n;
char *u;
if (reason == LEJPCB_ARRAY_END) {
lejp_parser_pop(ctx);
return 0;
}
if (reason == LEJPCB_ARRAY_START) {
if (!ctx->path_match)
lwsl_err("%s: ARRAY_START with ctx->path_match 0\n", __func__);
map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
if (map->type == LSMT_LIST)
lws_struct_lejp_push(ctx, args, map, NULL);
return 0;
}
if (ctx->pst_sp)
pmap = &args->map_st[ctx->pst_sp - 1]
[ctx->pst[ctx->pst_sp - 1].path_match - 1];
if (reason == LEJPCB_OBJECT_START) {
if (!ctx->path_match) {
ctx->pst[ctx->pst_sp].user = NULL;
return 0;
}
map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
n = args->map_entries_st[ctx->pst_sp];
if (map->type != LSMT_CHILD_PTR && map->type != LSMT_LIST) {
ctx->pst[ctx->pst_sp].user = NULL;
return 0;
}
pmap = map;
lws_struct_lejp_push(ctx, args, map, NULL);
}
if (reason == LEJPCB_OBJECT_END && pmap) {
if (pmap->type == LSMT_CHILD_PTR)
lejp_parser_pop(ctx);
if (ctx->pst_sp)
pmap = &args->map_st[ctx->pst_sp - 1]
[ctx->pst[ctx->pst_sp - 1].path_match - 1];
}
if (!ctx->path_match)
return 0;
map = &args->map_st[ctx->pst_sp][ctx->path_match - 1];
n = args->map_entries_st[ctx->pst_sp];
if (map->type == LSMT_SCHEMA) {
while (n--) {
if (strncmp(map->colname, ctx->buf, ctx->npos)) {
map++;
continue;
}
/* instantiate the correct toplevel object */
ch = lwsac_use_zero(&args->ac, map->aux,
args->ac_block_size);
if (!ch) {
lwsl_err("OOM\n");
return 1;
}
lws_struct_lejp_push(ctx, args, map, ch);
return 0;
}
lwsl_notice("%s: unknown schema %.*s, tried %d\n", __func__,
ctx->npos, ctx->buf,
(int)args->map_entries_st[ctx->pst_sp]);
goto cleanup;
}
if (!ctx->pst[ctx->pst_sp].user) {
struct lws_dll2_owner *owner;
struct lws_dll2 *list;
/* create list item object if none already */
if (!ctx->path_match || !pmap)
return 0;
map = &args->map_st[ctx->pst_sp - 1][ctx->path_match - 1];
n = args->map_entries_st[ctx->pst_sp - 1];
if (!ctx->pst_sp)
return 0;
if (pmap->type != LSMT_LIST && pmap->type != LSMT_CHILD_PTR)
return 1;
/* we need to create a child or array item object */
owner = (struct lws_dll2_owner *)
(((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs);
assert(pmap->aux);
/* instantiate one of the child objects */
ctx->pst[ctx->pst_sp].user = lwsac_use_zero(&args->ac,
pmap->aux, args->ac_block_size);
if (!ctx->pst[ctx->pst_sp].user) {
lwsl_err("OOM\n");
return 1;
}
lwsl_info("%s: created '%s' object size %d\n", __func__,
pmap->colname, (int)pmap->aux);
switch (pmap->type) {
case LSMT_LIST:
list = (struct lws_dll2 *)
((char *)ctx->pst[ctx->pst_sp].user +
pmap->ofs_clist);
lws_dll2_add_tail(list, owner);
break;
case LSMT_CHILD_PTR:
*((void **)owner) = ctx->pst[ctx->pst_sp].user;
break;
default:
assert(0);
break;
}
}
if (!ctx->path_match)
return 0;
if (reason == LEJPCB_VAL_STR_CHUNK) {
lejp_collation_t *coll;
/* don't cache stuff we are going to ignore */
if (map->type == LSMT_STRING_CHAR_ARRAY &&
args->chunks_length >= map->aux)
return 0;
coll = lwsac_use_zero(&args->ac_chunks, sizeof(*coll),
sizeof(*coll));
if (!coll) {
lwsl_err("%s: OOT\n", __func__);
return 1;
}
coll->chunks.prev = NULL;
coll->chunks.next = NULL;
coll->chunks.owner = NULL;
coll->len = ctx->npos;
lws_dll2_add_tail(&coll->chunks, &args->chunks_owner);
memcpy(coll->buf, ctx->buf, ctx->npos);
args->chunks_length += ctx->npos;
return 0;
}
if (reason != LEJPCB_VAL_STR_END && reason != LEJPCB_VAL_NUM_INT &&
reason != LEJPCB_VAL_TRUE && reason != LEJPCB_VAL_FALSE)
return 0;
/* this is the end of the string */
if (ctx->pst[ctx->pst_sp].user && pmap && pmap->type == LSMT_CHILD_PTR) {
void **pp = (void **)
(((char *)ctx->pst[ctx->pst_sp - 1].user) + pmap->ofs);
*pp = ctx->pst[ctx->pst_sp].user;
}
u = (char *)ctx->pst[ctx->pst_sp].user;
if (!u)
u = (char *)ctx->pst[ctx->pst_sp - 1].user;
{
char **pp, *s;
size_t lim, b;
long long li;
switch (map->type) {
case LSMT_SIGNED:
if (map->aux == sizeof(signed char)) {
signed char *pc;
pc = (signed char *)(u + map->ofs);
*pc = (signed char)atoi(ctx->buf);
break;
}
if (map->aux == sizeof(int)) {
int *pi;
pi = (int *)(u + map->ofs);
*pi = atoi(ctx->buf);
break;
}
if (map->aux == sizeof(long)) {
long *pl;
pl = (long *)(u + map->ofs);
*pl = atol(ctx->buf);
} else {
long long *pll;
pll = (long long *)(u + map->ofs);
*pll = atoll(ctx->buf);
}
break;
case LSMT_UNSIGNED:
if (map->aux == sizeof(unsigned char)) {
unsigned char *pc;
pc = (unsigned char *)(u + map->ofs);
*pc = (unsigned char)(unsigned int)atoi(ctx->buf);
break;
}
if (map->aux == sizeof(unsigned int)) {
unsigned int *pi;
pi = (unsigned int *)(u + map->ofs);
*pi = (unsigned int)atoi(ctx->buf);
break;
}
if (map->aux == sizeof(unsigned long)) {
unsigned long *pl;
pl = (unsigned long *)(u + map->ofs);
*pl = (unsigned long)atol(ctx->buf);
} else {
unsigned long long *pll;
pll = (unsigned long long *)(u + map->ofs);
*pll = (unsigned long long)atoll(ctx->buf);
}
break;
case LSMT_BOOLEAN:
li = reason == LEJPCB_VAL_TRUE;
if (map->aux == sizeof(char)) {
char *pc;
pc = (char *)(u + map->ofs);
*pc = (char)li;
break;
}
if (map->aux == sizeof(int)) {
int *pi;
pi = (int *)(u + map->ofs);
*pi = (int)li;
} else {
uint64_t *p64;
p64 = (uint64_t *)(u + map->ofs);
*p64 = (uint64_t)li;
}
break;
case LSMT_STRING_CHAR_ARRAY:
s = (char *)(u + map->ofs);
lim = map->aux - 1;
goto chunk_copy;
case LSMT_STRING_PTR:
pp = (char **)(u + map->ofs);
lim = args->chunks_length + ctx->npos;
s = lwsac_use(&args->ac, lim + 1, args->ac_block_size);
if (!s)
goto cleanup;
*pp = s;
chunk_copy:
s[lim] = '\0';
/* copy up to lim from the string chunk ac first */
lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
args->chunks_owner.head) {
lejp_collation_t *coll = (lejp_collation_t *)p;
if (lim) {
b = (unsigned int)coll->len;
if (b > lim)
b = lim;
memcpy(s, coll->buf, b);
s += b;
lim -= b;
}
} lws_end_foreach_dll_safe(p, p1);
lwsac_free(&args->ac_chunks);
args->chunks_owner.count = 0;
args->chunks_owner.head = NULL;
args->chunks_owner.tail = NULL;
if (lim) {
b = ctx->npos;
if (b > lim)
b = lim;
memcpy(s, ctx->buf, b);
s[b] = '\0';
}
break;
default:
break;
}
}
if (args->cb)
args->cb(args->dest, args->cb_arg);
return 0;
cleanup:
lwsl_notice("%s: cleanup\n", __func__);
lwsac_free(&args->ac_chunks);
args->chunks_owner.count = 0;
args->chunks_owner.head = NULL;
args->chunks_owner.tail = NULL;
return 1;
}
static const char * schema[] = { "schema" };
int
lws_struct_json_init_parse(struct lejp_ctx *ctx, lejp_callback cb, void *user)
{
/*
* By default we are looking to match on a toplevel member called
* "schema", against an LSM_SCHEMA
*/
if (!cb)
cb = lws_struct_schema_only_lejp_cb;
lejp_construct(ctx, cb, user, schema, 1);
ctx->path_stride = sizeof(lws_struct_map_t);
return 0;
}
lws_struct_serialize_t *
lws_struct_json_serialize_create(const lws_struct_map_t *map,
size_t map_entries, int flags,
const void *ptoplevel)
{
lws_struct_serialize_t *js = lws_zalloc(sizeof(*js), __func__);
lws_struct_serialize_st_t *j;
if (!js)
return NULL;
js->flags = flags;
j = &js->st[0];
j->map = map;
j->map_entries = map_entries;
j->obj = ptoplevel;
j->idt = 0;
return js;
}
void
lws_struct_json_serialize_destroy(lws_struct_serialize_t **pjs)
{
if (!*pjs)
return;
lws_free(*pjs);
*pjs = NULL;
}
static void
lws_struct_pretty(lws_struct_serialize_t *js, uint8_t **pbuf, size_t *plen)
{
if (js->flags & LSSERJ_FLAG_PRETTY) {
int n;
*(*pbuf)++ = '\n';
(*plen)--;
for (n = 0; n < js->st[js->sp].idt; n++) {
*(*pbuf)++ = ' ';
(*plen)--;
}
}
}
lws_struct_json_serialize_result_t
lws_struct_json_serialize(lws_struct_serialize_t *js, uint8_t *buf,
size_t len, size_t *written)
{
lws_struct_serialize_st_t *j;
const lws_struct_map_t *map;
size_t budget = 0, olen = len, m;
struct lws_dll2_owner *o;
unsigned long long uli;
const char *q;
const void *p;
char dbuf[72];
long long li;
int n, used;
*written = 0;
*buf = '\0';
while (len > sizeof(dbuf) + 20) {
j = &js->st[js->sp];
map = &j->map[j->map_entry];
q = j->obj + map->ofs;
/* early check if the entry should be elided */
switch (map->type) {
case LSMT_STRING_CHAR_ARRAY:
if (!q)
goto up;
break;
case LSMT_STRING_PTR:
case LSMT_CHILD_PTR:
q = (char *)*(char **)q;
if (!q)
goto up;
break;
case LSMT_LIST:
o = (struct lws_dll2_owner *)q;
p = j->dllpos = lws_dll2_get_head(o);
if (!p)
goto up;
break;
case LSMT_BLOB_PTR:
goto up;
default:
break;
}
if (j->subsequent) {
*buf++ = ',';
len--;
lws_struct_pretty(js, &buf, &len);
}
j->subsequent = 1;
if (map->type != LSMT_SCHEMA && !js->offset) {
n = lws_snprintf((char *)buf, len, "\"%s\":",
map->colname);
buf += n;
len = len - (unsigned int)n;
if (js->flags & LSSERJ_FLAG_PRETTY) {
*buf++ = ' ';
len--;
}
}
switch (map->type) {
case LSMT_BOOLEAN:
case LSMT_UNSIGNED:
if (map->aux == sizeof(char)) {
uli = *(unsigned char *)q;
} else {
if (map->aux == sizeof(int)) {
uli = *(unsigned int *)q;
} else {
if (map->aux == sizeof(long))
uli = *(unsigned long *)q;
else
uli = *(unsigned long long *)q;
}
}
q = dbuf;
if (map->type == LSMT_BOOLEAN) {
budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf),
"%s", uli ? "true" : "false");
} else
budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf),
"%llu", uli);
break;
case LSMT_SIGNED:
if (map->aux == sizeof(signed char)) {
li = (long long)*(signed char *)q;
} else {
if (map->aux == sizeof(int)) {
li = (long long)*(int *)q;
} else {
if (map->aux == sizeof(long))
li = (long long)*(long *)q;
else
li = *(long long *)q;
}
}
q = dbuf;
budget = (unsigned int)lws_snprintf(dbuf, sizeof(dbuf), "%lld", li);
break;
case LSMT_STRING_CHAR_ARRAY:
case LSMT_STRING_PTR:
if (!js->offset) {
*buf++ = '\"';
len--;
}
break;
case LSMT_LIST:
*buf++ = '[';
len--;
if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH)
return LSJS_RESULT_ERROR;
/* add a stack level to handle parsing array members */
o = (struct lws_dll2_owner *)q;
p = j->dllpos = lws_dll2_get_head(o);
if (!j->dllpos) {
*buf++ = ']';
len--;
goto up;
}
n = j->idt;
j = &js->st[++js->sp];
j->idt = (char)(n + 2);
j->map = map->child_map;
j->map_entries = map->child_map_size;
j->size = map->aux;
j->subsequent = 0;
j->map_entry = 0;
lws_struct_pretty(js, &buf, &len);
*buf++ = '{';
len--;
lws_struct_pretty(js, &buf, &len);
if (p)
j->obj = ((char *)p) - j->map->ofs_clist;
else
j->obj = NULL;
continue;
case LSMT_CHILD_PTR:
if (js->sp + 1 == LEJP_MAX_PARSING_STACK_DEPTH)
return LSJS_RESULT_ERROR;
/* add a stack level to handle parsing child members */
n = j->idt;
j = &js->st[++js->sp];
j->idt = (char)(n + 2);
j->map = map->child_map;
j->map_entries = map->child_map_size;
j->size = map->aux;
j->subsequent = 0;
j->map_entry = 0;
*buf++ = '{';
len--;
lws_struct_pretty(js, &buf, &len);
j->obj = q;
continue;
case LSMT_SCHEMA:
q = dbuf;
*buf++ = '{';
len--;
j = &js->st[++js->sp];
lws_struct_pretty(js, &buf, &len);
if (!(js->flags & LSSERJ_FLAG_OMIT_SCHEMA)) {
budget = (unsigned int)lws_snprintf(dbuf, 15, "\"schema\":");
if (js->flags & LSSERJ_FLAG_PRETTY)
dbuf[budget++] = ' ';
budget += (unsigned int)lws_snprintf(dbuf + budget,
sizeof(dbuf) - budget,
"\"%s\"", map->colname);
}
if (js->sp != 1)
return LSJS_RESULT_ERROR;
j->map = map->child_map;
j->map_entries = map->child_map_size;
j->size = map->aux;
j->subsequent = 0;
j->map_entry = 0;
j->obj = js->st[js->sp - 1].obj;
j->dllpos = NULL;
if (!(js->flags & LSSERJ_FLAG_OMIT_SCHEMA))
/* we're actually at the same level */
j->subsequent = 1;
j->idt = 1;
break;
default:
break;
}
switch (map->type) {
case LSMT_STRING_CHAR_ARRAY:
case LSMT_STRING_PTR:
/*
* This is a bit tricky... we have to escape the string
* which may 6x its length depending on what the
* contents are.
*
* We offset the unescaped string starting point first
*/
q += js->offset;
budget = strlen(q); /* how much unescaped is left */
/*
* This is going to escape as much as it can fit, and
* let us know the amount of input that was consumed
* in "used".
*/
lws_json_purify((char *)buf, q, (int)len, &used);
m = strlen((const char *)buf);
buf += m;
len -= m;
js->remaining = budget - (unsigned int)used;
js->offset = (unsigned int)used;
if (!js->remaining)
js->offset = 0;
break;
default:
q += js->offset;
budget -= js->remaining;
if (budget > len) {
js->remaining = budget - len;
js->offset = len;
budget = len;
} else {
js->remaining = 0;
js->offset = 0;
}
memcpy(buf, q, budget);
buf += budget;
*buf = '\0';
len -= budget;
break;
}
switch (map->type) {
case LSMT_STRING_CHAR_ARRAY:
case LSMT_STRING_PTR:
*buf++ = '\"';
len--;
break;
case LSMT_SCHEMA:
continue;
default:
break;
}
if (js->remaining)
continue;
up:
if (++j->map_entry < j->map_entries)
continue;
if (!js->sp)
continue;
js->sp--;
if (!js->sp) {
lws_struct_pretty(js, &buf, &len);
*buf++ = '}';
len--;
lws_struct_pretty(js, &buf, &len);
break;
}
js->offset = 0;
j = &js->st[js->sp];
map = &j->map[j->map_entry];
if (map->type == LSMT_CHILD_PTR) {
lws_struct_pretty(js, &buf, &len);
*buf++ = '}';
len--;
/* we have done the singular child pointer */
js->offset = 0;
goto up;
}
if (map->type != LSMT_LIST)
continue;
/*
* we are coming back up to an array map, it means we should
* advance to the next array member if there is one
*/
lws_struct_pretty(js, &buf, &len);
*buf++ = '}';
len--;
p = j->dllpos = j->dllpos->next;
if (j->dllpos) {
/*
* there was another item in the array to do... let's
* move on to that and do it
*/
*buf++ = ',';
len--;
lws_struct_pretty(js, &buf, &len);
js->offset = 0;
j = &js->st[++js->sp];
j->map_entry = 0;
map = &j->map[j->map_entry];
*buf++ = '{';
len--;
lws_struct_pretty(js, &buf, &len);
j->subsequent = 0;
j->obj = ((char *)p) - j->map->ofs_clist;
continue;
}
/* there are no further items in the array */
js->offset = 0;
lws_struct_pretty(js, &buf, &len);
*buf++ = ']';
len--;
goto up;
}
*written = olen - len;
*buf = '\0'; /* convenience, a NUL after the official end */
return LSJS_RESULT_FINISH;
}

View File

@ -0,0 +1,558 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <libwebsockets.h>
#include <private-lib-core.h>
#include <sqlite3.h>
/*
* we get one of these per matching result from the query
*/
static int
lws_struct_sq3_deser_cb(void *priv, int cols, char **cv, char **cn)
{
lws_struct_args_t *a = (lws_struct_args_t *)priv;
char *u = lwsac_use_zero(&a->ac, a->dest_len, a->ac_block_size);
lws_dll2_owner_t *o = (lws_dll2_owner_t *)a->cb_arg;
const lws_struct_map_t *map = a->map_st[0];
int n, mems = (int)(ssize_t)a->map_entries_st[0];
long long li;
size_t lim;
char **pp;
char *s;
if (!u) {
lwsl_err("OOM\n");
return 1;
}
lws_dll2_add_tail((lws_dll2_t *)((char *)u + a->toplevel_dll2_ofs), o);
while (mems--) {
for (n = 0; n < cols; n++) {
if (!cv[n] || strcmp(cn[n], map->colname))
continue;
switch (map->type) {
case LSMT_SIGNED:
if (map->aux == sizeof(signed char)) {
signed char *pc;
pc = (signed char *)(u + map->ofs);
*pc = (signed char)atoi(cv[n]);
break;
}
if (map->aux == sizeof(short)) {
short *ps;
ps = (short *)(u + map->ofs);
*ps = (short)atoi(cv[n]);
break;
}
if (map->aux == sizeof(int)) {
int *pi;
pi = (int *)(u + map->ofs);
*pi = (int)atoll(cv[n]); /* 32-bit OS */
break;
}
if (map->aux == sizeof(long)) {
long *pl;
pl = (long *)(u + map->ofs);
*pl = (long)atoll(cv[n]); /* 32-bit OS */
break;
}
{
long long *pll;
pll = (long long *)(u + map->ofs);
*pll = atoll(cv[n]);
}
break;
case LSMT_UNSIGNED:
if (map->aux == sizeof(unsigned char)) {
unsigned char *pc;
pc = (unsigned char *)(u + map->ofs);
*pc = (unsigned char)(unsigned int)atoi(cv[n]);
break;
}
if (map->aux == sizeof(unsigned short)) {
unsigned short *ps;
ps = (unsigned short *)(u + map->ofs);
*ps = (unsigned short)atoi(cv[n]);
break;
}
if (map->aux == sizeof(unsigned int)) {
unsigned int *pi;
pi = (unsigned int *)(u + map->ofs);
*pi = (unsigned int)atoi(cv[n]);
break;
}
if (map->aux == sizeof(unsigned long)) {
unsigned long *pl;
pl = (unsigned long *)(u + map->ofs);
*pl = (unsigned long)atol(cv[n]);
break;
}
{
unsigned long long *pll;
pll = (unsigned long long *)(u + map->ofs);
*pll = (unsigned long long)atoll(cv[n]);
}
break;
case LSMT_BOOLEAN:
li = 0;
if (!strcmp(cv[n], "true") ||
!strcmp(cv[n], "TRUE") || cv[n][0] == '1')
li = 1;
if (map->aux == sizeof(char)) {
char *pc;
pc = (char *)(u + map->ofs);
*pc = (char)li;
break;
}
if (map->aux == sizeof(int)) {
int *pi;
pi = (int *)(u + map->ofs);
*pi = (int)li;
} else {
uint64_t *p64;
p64 = (uint64_t *)(u + map->ofs);
*p64 = (uint64_t)li;
}
break;
case LSMT_STRING_CHAR_ARRAY:
s = (char *)(u + map->ofs);
lim = map->aux;
lws_strncpy(s, cv[n], lim);
break;
case LSMT_STRING_PTR:
pp = (char **)(u + map->ofs);
lim = strlen(cv[n]);
s = lwsac_use(&a->ac, lim + 1, a->ac_block_size);
if (!s)
return 1;
*pp = s;
memcpy(s, cv[n], lim);
s[lim] = '\0';
break;
default:
break;
}
}
map++;
}
return 0;
}
/*
* Call this with an LSM_SCHEMA map, its colname is the table name and its
* type information describes the toplevel type. Schema is dereferenced and
* put in args before the actual sq3 query, which is given the child map.
*/
int
lws_struct_sq3_deserialize(sqlite3 *pdb, const char *filter, const char *order,
const lws_struct_map_t *schema, lws_dll2_owner_t *o,
struct lwsac **ac, int start, int _limit)
{
int limit = _limit < 0 ? -_limit : _limit;
char s[768], results[512], where[250];
lws_struct_args_t a;
int n, m;
if (!order)
order = "_lws_idx";
memset(&a, 0, sizeof(a));
a.ac = *ac;
a.cb_arg = o; /* lws_dll2_owner tracking query result objects */
a.map_st[0] = schema->child_map;
a.map_entries_st[0] = schema->child_map_size;
a.dest_len = schema->aux; /* size of toplevel object to allocate */
a.toplevel_dll2_ofs = schema->ofs;
lws_dll2_owner_clear(o);
/*
* Explicitly list the columns instead of use *, so we can skip blobs
*/
m = 0;
for (n = 0; n < (int)schema->child_map_size; n++)
m += lws_snprintf(&results[m], sizeof(results) - (unsigned int)n - 1,
"%s%c", schema->child_map[n].colname,
n + 1 == (int)schema->child_map_size ? ' ' : ',');
where[0] = '\0';
lws_snprintf(where, sizeof(where), " where _lws_idx >= %llu %s",
(unsigned long long)start, filter ? filter : "");
lws_snprintf(s, sizeof(s) - 1, "select %s "
"from %s %s order by %s %slimit %d;", results,
schema->colname, where, order,
_limit < 0 ? "desc " : "", limit);
if (sqlite3_exec(pdb, s, lws_struct_sq3_deser_cb, &a, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: fail %s\n", __func__, sqlite3_errmsg(pdb), s);
lwsac_free(&a.ac);
return -1;
}
*ac = a.ac;
return 0;
}
/*
* This takes a struct and turns it into an sqlite3 UPDATE, using the given
* schema... which has one LSM_SCHEMA_DLL2 entry wrapping the actual schema
*/
static int
_lws_struct_sq3_ser_one(sqlite3 *pdb, const lws_struct_map_t *schema,
uint32_t idx, void *st)
{
const lws_struct_map_t *map = schema->child_map;
int n, m, pk = 0, nentries = (int)(ssize_t)schema->child_map_size, nef = 0, did;
size_t sql_est = 46 + strlen(schema->colname) + 1;
/* "insert into (_lws_idx, ) values (00000001,);" ...
* plus the table name */
uint8_t *stb = (uint8_t *)st;
const char *p;
char *sql;
/*
* Figure out effective number of columns, exluding BLOB.
*
* The first UNSIGNED is a hidden index. Blobs are not handled by
* lws_struct except to create the column in the schema.
*/
pk = 0;
nef = 0;
for (n = 0; n < nentries; n++) {
if (!pk && map[n].type == LSMT_UNSIGNED) {
pk = 1;
continue;
}
if (map[n].type == LSMT_BLOB_PTR)
continue;
nef++;
}
/*
* Figure out an estimate for the length of the populated sqlite
* command, and then malloc it up
*/
for (n = 0; n < nentries; n++) {
sql_est += strlen(map[n].colname) + 2;
switch (map[n].type) {
case LSMT_SIGNED:
case LSMT_UNSIGNED:
case LSMT_BOOLEAN:
switch (map[n].aux) {
case 1:
sql_est += 3 + 2;
break;
case 2:
sql_est += 5 + 2;
break;
case 4:
sql_est += 10 + 2;
break;
case 8:
sql_est += 20 + 2;
break;
}
if (map[n].type == LSMT_SIGNED)
sql_est++; /* minus sign */
break;
case LSMT_STRING_CHAR_ARRAY:
sql_est += (unsigned int)lws_sql_purify_len((const char *)st +
map[n].ofs) + 2;
break;
case LSMT_STRING_PTR:
p = *((const char * const *)&stb[map[n].ofs]);
sql_est += (unsigned int)((p ? lws_sql_purify_len(p) : 0) + 2);
break;
case LSMT_BLOB_PTR:
/* we don't deal with blobs actually */
sql_est -= strlen(map[n].colname) + 2;
break;
default:
lwsl_err("%s: unsupported type\n", __func__);
assert(0);
break;
}
}
sql = malloc(sql_est);
if (!sql)
return -1;
m = lws_snprintf(sql, sql_est, "insert into %s(_lws_idx, ",
schema->colname);
/*
* First explicit integer type is primary key autoincrement, should
* not be specified
*/
pk = 0;
did = 0;
for (n = 0; n < nentries; n++) {
if (!pk && map[n].type == LSMT_UNSIGNED) {
pk = 1;
continue;
}
if (map[n].type == LSMT_BLOB_PTR)
continue;
did++;
m += lws_snprintf(sql + m, sql_est - (unsigned int)m,
did == nef ? "%s" : "%s, ",
map[n].colname);
}
m += lws_snprintf(sql + m, sql_est - (unsigned int)m, ") values(%u, ", idx);
pk = 0;
did = 0;
for (n = 0; n < nentries; n++) {
uint64_t uu64;
size_t q;
if (!pk && map[n].type == LSMT_UNSIGNED) {
pk = 1;
continue;
}
switch (map[n].type) {
case LSMT_SIGNED:
case LSMT_UNSIGNED:
case LSMT_BOOLEAN:
uu64 = 0;
for (q = 0; q < map[n].aux; q++)
uu64 |= ((uint64_t)stb[map[n].ofs + q] <<
(q << 3));
if (map[n].type == LSMT_SIGNED)
m += lws_snprintf(sql + m, sql_est - (unsigned int)m, "%lld",
(long long)(int64_t)uu64);
else
m += lws_snprintf(sql + m, sql_est - (unsigned int)m, "%llu",
(unsigned long long)uu64);
break;
case LSMT_STRING_CHAR_ARRAY:
sql[m++] = '\'';
lws_sql_purify(sql + m, (const char *)&stb[map[n].ofs],
sql_est - (size_t)(ssize_t)m - 4);
m += (int)(ssize_t)strlen(sql + m);
sql[m++] = '\'';
break;
case LSMT_STRING_PTR:
p = *((const char * const *)&stb[map[n].ofs]);
sql[m++] = '\'';
if (p) {
lws_sql_purify(sql + m, p, sql_est - (unsigned int)m - 4);
m += (int)(ssize_t)strlen(sql + m);
}
sql[m++] = '\'';
break;
case LSMT_BLOB_PTR:
continue;
default:
lwsl_err("%s: unsupported type\n", __func__);
assert(0);
break;
}
did++;
if (did != nef) {
if (sql_est - (unsigned int)m < 6)
return -1;
sql[m++] = ',';
sql[m++] = ' ';
}
}
lws_snprintf(sql + m, sql_est - (unsigned int)m, ");");
n = sqlite3_exec(pdb, sql, NULL, NULL, NULL);
if (n != SQLITE_OK) {
lwsl_err("%s\n", sql);
free(sql);
lwsl_err("%s: %s: fail\n", __func__, sqlite3_errmsg(pdb));
return -1;
}
free(sql);
return 0;
}
int
lws_struct_sq3_serialize(sqlite3 *pdb, const lws_struct_map_t *schema,
lws_dll2_owner_t *owner, uint32_t manual_idx)
{
uint32_t idx = manual_idx;
lws_start_foreach_dll(struct lws_dll2 *, p, owner->head) {
void *item = (void *)((uint8_t *)p - schema->ofs_clist);
if (_lws_struct_sq3_ser_one(pdb, schema, idx++, item))
return 1;
} lws_end_foreach_dll(p);
return 0;
}
int
lws_struct_sq3_create_table(sqlite3 *pdb, const lws_struct_map_t *schema)
{
const lws_struct_map_t *map = schema->child_map;
int map_size = (int)(ssize_t)schema->child_map_size, subsequent = 0;
char s[2048], *p = s, *end = &s[sizeof(s) - 1],
*pri = " primary key autoincrement", *use;
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p),
"create table if not exists %s (_lws_idx integer, ",
schema->colname);
while (map_size--) {
if (map->type > LSMT_STRING_PTR && map->type != LSMT_BLOB_PTR) {
map++;
continue;
}
if (subsequent && (end - p) > 4) {
*p++ = ',';
*p++ = ' ';
}
subsequent = 1;
if (map->type == LSMT_BLOB_PTR) {
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p), "%s blob", map->colname);
} else {
if (map->type < LSMT_STRING_CHAR_ARRAY) {
use = "";
if (map->colname[0] != '_') /* _lws_idx is not primary key */
use = pri;
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p), "%s integer%s",
map->colname, use);
if (map->colname[0] != '_')
pri = "";
} else
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p), "%s varchar",
map->colname);
}
map++;
}
p += lws_snprintf(p, (unsigned int)lws_ptr_diff(end, p), ");");
if (sqlite3_exec(pdb, s, NULL, NULL, NULL) != SQLITE_OK) {
lwsl_err("%s: %s: fail\n", __func__, sqlite3_errmsg(pdb));
return -1;
}
return 0;
}
int
lws_struct_sq3_open(struct lws_context *context, const char *sqlite3_path,
char create_if_missing, sqlite3 **pdb)
{
#if !defined(WIN32)
uid_t uid = 0;
gid_t gid = 0;
#endif
if (sqlite3_open_v2(sqlite3_path, pdb,
SQLITE_OPEN_READWRITE |
(create_if_missing ? SQLITE_OPEN_CREATE : 0),
NULL) != SQLITE_OK) {
lwsl_info("%s: Unable to open db %s: %s\n",
__func__, sqlite3_path, sqlite3_errmsg(*pdb));
return 1;
}
#if !defined(WIN32)
lws_get_effective_uid_gid(context, &uid, &gid);
if (uid)
if (chown(sqlite3_path, uid, gid))
lwsl_err("%s: failed to chown %s\n", __func__, sqlite3_path);
chmod(sqlite3_path, 0600);
lwsl_debug("%s: created %s owned by %u:%u mode 0600\n", __func__,
sqlite3_path, (unsigned int)uid, (unsigned int)gid);
#else
lwsl_debug("%s: created %s\n", __func__, sqlite3_path);
#endif
sqlite3_extended_result_codes(*pdb, 1);
return 0;
}
int
lws_struct_sq3_close(sqlite3 **pdb)
{
int n;
if (!*pdb)
return 0;
n = sqlite3_close(*pdb);
if (n != SQLITE_OK) {
/*
* trouble...
*/
lwsl_err("%s: failed to close: %d\n", __func__, n);
return 1;
}
*pdb = NULL;
return 0;
}

View File

@ -0,0 +1,148 @@
## LWS Allocated Chunks
![lwsac flow](/doc-assets/lwsac.svg)
These apis provide a way to manage a linked-list of allocated chunks...
[ HEAD alloc ] -> [ next alloc ] -> [ next alloc ] -> [ curr alloc ]
... and sub-allocate trivially inside the chunks. These sub-allocations are
not tracked by lwsac at all, there is a "used" high-water mark for each chunk
that's simply advanced by the amount sub-allocated. If the allocation size
matches the platform pointer alignment, there is zero overhead to sub-allocate
(otherwise the allocation is padded to the next platform pointer alignment
automatically).
If you have an unknown amount of relatively little things to allocate, including
strings or other unstructured data, lwsac is significantly more efficient than
individual allocations using malloc or so.
[lwsac full public api](https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-lwsac.h)
## lwsac_use() api
```
/**
* lwsac_use - allocate / use some memory from a lwsac
*
* \param head: pointer to the lwsac list object
* \param ensure: the number of bytes we want to use
* \param chunk_size: 0, or the size of the chunk to (over)allocate if
* what we want won't fit in the current tail chunk. If
* 0, the default value of 4000 is used. If ensure is
* larger, it is used instead.
*
* This also serves to init the lwsac if *head is NULL. Basically it does
* whatever is necessary to return you a pointer to ensure bytes of memory
* reserved for the caller.
*
* Returns NULL if OOM.
*/
LWS_VISIBLE LWS_EXTERN void *
lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size);
```
When you make an sub-allocation using `lwsac_use()`, you can either
set the `chunk_size` arg to zero, defaulting to 4000, or a specific chunk size.
In the event the requested sub-allocation exceeds the chunk size, the chunk
size is increated to match it automatically for this allocation only.
Subsequent `lwsac_use()` calls will advance internal pointers to use up the
remaining space inside the current chunk if possible; if not enough remaining
space it is skipped, a new allocation is chained on and the request pointed to
there.
Lwsac does not store information about sub-allocations. There is really zero
overhead for individual sub-allocations (unless their size is not
pointer-aligned, in which case the actual amount sub-allocated is rounded up to
the next pointer alignment automatically). For structs, which are pointer-
aligned naturally, and a chunk size relatively large for the sub-allocation
size, lwsac is extremely efficient even for huge numbers of small allocations.
This makes lwsac very effective when the total amount of allocation needed is
not known at the start and may be large... it will simply add on chunks to cope
with whatever happens.
## lwsac_free() api
```
/**
* lwsac_free - deallocate all chunks in the lwsac and set head NULL
*
* \param head: pointer to the lwsac list object
*
* This deallocates all chunks in the lwsac, then sets *head to NULL. All
* lwsac_use() pointers are invalidated in one hit without individual frees.
*/
LWS_VISIBLE LWS_EXTERN void
lwsac_free(struct lwsac **head);
```
When you are finished with the lwsac, you simply free the chain of allocated
chunks using lwsac_free() on the lwsac head. There's no tracking or individual
destruction of suballocations - the whole chain of chunks the suballocations
live in are freed and invalidated all together.
If the structs stored in the lwsac allocated things **outside** the lwsac, then the
user must unwind through them and perform the frees. But the idea of lwsac is
things stored in the lwsac also suballocate into the lwsac, and point into the
lwsac if they need to, avoiding any need to visit them during destroy. It's
like clearing up after a kids' party by gathering up a disposable tablecloth:
no matter what was left on the table, it's all gone in one step.
## `lws_list_ptr` helpers
```
/* sort may be NULL if you don't care about order */
LWS_VISIBLE LWS_EXTERN void
lws_list_ptr_insert(lws_list_ptr *phead, lws_list_ptr *add,
lws_list_ptr_sort_func_t sort);
```
A common pattern needed with sub-allocated structs is they are on one or more
linked-list. To make that simple to do cleanly, `lws_list...` apis are provided
along with a generic insertion function that can take a sort callback. These
allow a struct to participate on multiple linked-lists simultaneously.
## common const string and blob folding
In some cases the input to be stored in the lwsac may repeat the same tokens
multiple times... if the pattern is to store the string or blob in the lwsac
and then point to it, you can make use of a helper api
```
uint8_t *
lwsac_scan_extant(struct lwsac *head, uint8_t *find, size_t len, int nul);
```
This lets you check in all previous used parts of the lwsac for the same
string or blob, plus optionally a terminal NUL afterwards. If not found,
it returns `NULL` and you can copy it into the lwsac as usual. If it is
found, a pointer is returned, and you can use this directly without copying
the string or blob in again.
## optimizations to minimize overhead
If the lwsac will persist in the system for some time, it's desirable to reduce
the memory needed as overhead. Overhead is created
- once per chunk... in addition to the malloc overhead, there's an lwsac
chunk header of 2 x pointers and 2 x size_t
- at the unused part at the end that was allocated but not used
A good strategy is to make the initial allocation reflect the minimum expected
size of the overall lwsac in one hit. Then use a chunk size that is a tradeoff
between the number of chunks that might be needed and the fact that on average,
you can expect to waste half a chunk. For example if the storage is typically
between 4K - 6K, you could allocate 4K or 4.5K for the first chunk and then fill
in using 256 or 512 byte chunks.
You can measure the overhead in an lwsac using `lwsac_total_overhead()`.
The lwsac apis look first in the unused part of previous chunks, if any, and
will place new allocations there preferentially if they fit. This helps for the
case lwsac was forced to allocate a new chunk because you asked for something
large, while there was actually significant free space left in the old chunk,
just not enough for that particular allocation. Subsequent lwsac use can then
"backfill" smaller things there to make best use of allocated space.

View File

@ -0,0 +1,218 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT)
#include "private-lib-core.h"
#include "private-lib-misc-lwsac.h"
/*
* Helper for caching a file in memory in a lac, but also to check at intervals
* no less than 5s if the file is still fresh.
*
* Set *cache to NULL the first time before calling.
*
* You should call this each time before using the cache... if it's
*
* - less than 5s since the last freshness check, and
* - the file is already in memory
*
* it just returns with *cache left alone; this costs very little. You should
* call `lwsac_use_cached_file_start()` and `lwsac_use_cached_file_end()`
* to lock the cache against deletion while you are using it.
*
* If it's
*
* - at least 5s since the last freshness check, and
* - the file timestamp has changed
*
* then
*
* - the file is reloaded into a new lac and *cache set to that
*
* - the old cache lac, if any, is detached (so it will be freed when its
* reference count reaches zero, or immediately if nobody has it)
*
* Note the call can fail due to OOM or filesystem issue at any time.
*
*
* After the LAC header there is stored a `struct cached_file_info` and then
* the raw file contents. *
*
* [LAC header]
* [struct cached_file_info]
* [file contents] <--- *cache is set to here
*
* The api returns a lwsac_cached_file_t type offset to point to the file
* contents. Helpers for reference counting and freeing are also provided
* that take that type and know how to correct it back to operate on the LAC.
*/
#define cache_file_to_lac(c) ((struct lwsac *)((char *)c - \
sizeof(struct cached_file_info) - \
sizeof(struct lwsac_head) - \
sizeof(struct lwsac)))
void
lwsac_use_cached_file_start(lwsac_cached_file_t cache)
{
struct lwsac *lac = cache_file_to_lac(cache);
struct lwsac_head *lachead = (struct lwsac_head *)&lac->head[1];
lachead->refcount++;
// lwsl_debug("%s: html refcount: %d\n", __func__, lachead->refcount);
}
void
lwsac_use_cached_file_end(lwsac_cached_file_t *cache)
{
struct lwsac *lac;
struct lwsac_head *lachead;
if (!cache || !*cache)
return;
lac = cache_file_to_lac(*cache);
lachead = (struct lwsac_head *)&lac->head[1];
if (!lachead->refcount)
lwsl_err("%s: html refcount zero on entry\n", __func__);
if (lachead->refcount && !--lachead->refcount && lachead->detached) {
*cache = NULL; /* not usable any more */
lwsac_free(&lac);
}
}
void
lwsac_use_cached_file_detach(lwsac_cached_file_t *cache)
{
struct lwsac *lac = cache_file_to_lac(*cache);
struct lwsac_head *lachead = NULL;
if (lac) {
lachead = (struct lwsac_head *)&lac->head[1];
lachead->detached = 1;
if (lachead->refcount)
return;
}
*cache = NULL;
lwsac_free(&lac);
}
int
lwsac_cached_file(const char *filepath, lwsac_cached_file_t *cache, size_t *len)
{
struct cached_file_info *info = NULL;
lwsac_cached_file_t old = *cache;
struct lwsac *lac = NULL;
time_t t = time(NULL);
unsigned char *a;
struct stat s;
size_t all;
ssize_t rd;
int fd;
if (old) { /* we already have a cached copy of it */
info = (struct cached_file_info *)((*cache) - sizeof(*info));
if (t - info->last_confirm < 5)
/* we checked it as fresh less than 5s ago, use old */
return 0;
}
/*
* ...it's been 5s, we should check again on the filesystem
* that the file hasn't changed
*/
fd = open(filepath, O_RDONLY);
if (fd < 0) {
lwsl_err("%s: cannot open %s\n", __func__, filepath);
return 1;
}
if (fstat(fd, &s)) {
lwsl_err("%s: cannot stat %s\n", __func__, filepath);
goto bail;
}
if (old && s.st_mtime == info->s.st_mtime) {
/* it still seems to be the same as our cached one */
info->last_confirm = t;
close(fd);
return 0;
}
/*
* we either didn't cache it yet, or it has changed since we cached
* it... reload in a new lac and then detach the old lac.
*/
all = sizeof(*info) + (unsigned long)s.st_size + 2;
info = lwsac_use(&lac, all, all);
if (!info)
goto bail;
info->s = s;
info->last_confirm = t;
a = (unsigned char *)(info + 1);
*len = (unsigned long)s.st_size;
a[s.st_size] = '\0';
rd = read(fd, a, (unsigned long)s.st_size);
if (rd != s.st_size) {
lwsl_err("%s: cannot read %s (%d)\n", __func__, filepath,
(int)rd);
goto bail1;
}
close(fd);
*cache = (lwsac_cached_file_t)a;
if (old)
lwsac_use_cached_file_detach(&old);
return 0;
bail1:
lwsac_free(&lac);
bail:
close(fd);
return 1;
}
#endif

View File

@ -0,0 +1,358 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "private-lib-misc-lwsac.h"
void
lws_list_ptr_insert(lws_list_ptr *head, lws_list_ptr *add,
lws_list_ptr_sort_func_t sort_func)
{
while (sort_func && *head) {
if (sort_func(add, *head) <= 0)
break;
head = *head;
}
*add = *head;
*head = add;
}
size_t
lwsac_align(size_t length)
{
size_t align = sizeof(int *);
if (length & (align - 1))
length += align - (length & (align - 1));
return length;
}
size_t
lwsac_sizeof(int first)
{
return sizeof(struct lwsac) + (first ? sizeof(struct lwsac_head) : 0);
}
size_t
lwsac_get_tail_pos(struct lwsac *lac)
{
return lac->ofs;
}
struct lwsac *
lwsac_get_next(struct lwsac *lac)
{
return lac->next;
}
int
lwsac_extend(struct lwsac *head, size_t amount)
{
struct lwsac_head *lachead;
struct lwsac *bf;
assert(head);
lachead = (struct lwsac_head *)&head[1];
bf = lachead->curr;
assert(bf);
if (bf->alloc_size - bf->ofs < lwsac_align(amount))
return 1;
/* memset so constant folding never sees uninitialized data */
memset(((uint8_t *)bf) + bf->ofs, 0, lwsac_align(amount));
bf->ofs += lwsac_align(amount);
return 0;
}
static void *
_lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size, char backfill)
{
struct lwsac_head *lachead = NULL;
size_t ofs, alloc, al, hp;
struct lwsac *bf = *head;
if (bf)
lachead = (struct lwsac_head *)&bf[1];
al = lwsac_align(ensure);
/* backfill into earlier chunks if that is allowed */
if (backfill)
/*
* check if anything can take it, from the start
*/
while (bf) {
if (bf->alloc_size - bf->ofs >= ensure)
goto do_use;
bf = bf->next;
}
else {
/*
* If there's a current chunk, just check if he can take it
*/
if (lachead && lachead->curr) {
bf = lachead->curr;
if (bf->alloc_size - bf->ofs >= ensure)
goto do_use;
}
}
/* nothing can currently take it... so we must allocate */
hp = sizeof(*bf); /* always need the normal header part... */
if (!*head)
hp += sizeof(struct lwsac_head);
if (!chunk_size)
alloc = LWSAC_CHUNK_SIZE + hp;
else
alloc = chunk_size + hp;
/*
* If we get asked for something outside our expectation,
* increase the allocation to meet it
*/
if (al >= alloc - hp)
alloc = al + hp;
// lwsl_debug("%s: alloc %d for %d\n", __func__, (int)alloc, (int)ensure);
bf = malloc(alloc);
if (!bf) {
lwsl_err("%s: OOM trying to alloc %llud\n", __func__,
(unsigned long long)alloc);
return NULL;
}
/*
* belabouring the point... ofs is aligned to the platform's
* generic struct alignment at the start then
*/
bf->ofs = sizeof(*bf);
if (!*head) {
/*
* We are the first, head, entry...
*/
*head = bf;
/*
* ... allocate for the special head block
*/
bf->ofs += sizeof(*lachead);
lachead = (struct lwsac_head *)&bf[1];
memset(lachead, 0, sizeof(*lachead));
} else
if (lachead->curr)
lachead->curr->next = bf;
lachead->curr = bf;
bf->head = *head;
bf->next = NULL;
bf->alloc_size = alloc;
lachead->total_alloc_size += alloc;
lachead->total_blocks++;
do_use:
ofs = bf->ofs;
if (al > ensure)
/* zero down the alignment padding part */
memset((char *)bf + ofs + ensure, 0, al - ensure);
bf->ofs += al;
if (bf->ofs >= bf->alloc_size)
bf->ofs = bf->alloc_size;
return (char *)bf + ofs;
}
void *
lwsac_use(struct lwsac **head, size_t ensure, size_t chunk_size)
{
return _lwsac_use(head, ensure, chunk_size, 0);
}
void *
lwsac_use_backfill(struct lwsac **head, size_t ensure, size_t chunk_size)
{
return _lwsac_use(head, ensure, chunk_size, 1);
}
uint8_t *
lwsac_scan_extant(struct lwsac *head, uint8_t *find, size_t len, int nul)
{
while (head) {
uint8_t *pos = (uint8_t *)&head[1],
*end = ((uint8_t *)head) + head->ofs - len;
if (head->ofs - sizeof(*head) >= len)
while (pos < end) {
if (*pos == *find && (!nul || !pos[len]) &&
pos[len - 1] == find[len - 1] &&
!memcmp(pos, find, len))
/* found the blob */
return pos;
pos++;
}
head = head->next;
}
return NULL;
}
uint64_t
lwsac_total_overhead(struct lwsac *head)
{
uint64_t overhead = 0;
while (head) {
overhead += (head->alloc_size - head->ofs) + sizeof(*head);
head = head->next;
}
return overhead;
}
void *
lwsac_use_zero(struct lwsac **head, size_t ensure, size_t chunk_size)
{
void *p = lwsac_use(head, ensure, chunk_size);
if (p)
memset(p, 0, ensure);
return p;
}
void
lwsac_free(struct lwsac **head)
{
struct lwsac *it = *head;
*head = NULL;
// lwsl_debug("%s: head %p\n", __func__, *head);
while (it) {
struct lwsac *tmp = it->next;
free(it);
it = tmp;
}
}
void
lwsac_info(struct lwsac *head)
{
#if _LWS_ENABLED_LOGS & LLL_DEBUG
struct lwsac_head *lachead;
if (!head) {
lwsl_debug("%s: empty\n", __func__);
return;
}
lachead = (struct lwsac_head *)&head[1];
lwsl_debug("%s: lac %p: %dKiB in %d blocks\n", __func__, head,
(int)(lachead->total_alloc_size >> 10), lachead->total_blocks);
#endif
}
uint64_t
lwsac_total_alloc(struct lwsac *head)
{
struct lwsac_head *lachead;
if (!head)
return 0;
lachead = (struct lwsac_head *)&head[1];
return lachead->total_alloc_size;
}
void
lwsac_reference(struct lwsac *head)
{
struct lwsac_head *lachead = (struct lwsac_head *)&head[1];
lachead->refcount++;
lwsl_debug("%s: head %p: (det %d) refcount -> %d\n",
__func__, head, lachead->detached, lachead->refcount);
}
void
lwsac_unreference(struct lwsac **head)
{
struct lwsac_head *lachead;
if (!(*head))
return;
lachead = (struct lwsac_head *)&(*head)[1];
if (!lachead->refcount)
lwsl_warn("%s: refcount going below zero\n", __func__);
lachead->refcount--;
lwsl_debug("%s: head %p: (det %d) refcount -> %d\n",
__func__, *head, lachead->detached, lachead->refcount);
if (lachead->detached && !lachead->refcount) {
lwsl_debug("%s: head %p: FREED\n", __func__, *head);
lwsac_free(head);
}
}
void
lwsac_detach(struct lwsac **head)
{
struct lwsac_head *lachead;
if (!(*head))
return;
lachead = (struct lwsac_head *)&(*head)[1];
lachead->detached = 1;
if (!lachead->refcount) {
lwsl_debug("%s: head %p: FREED\n", __func__, *head);
lwsac_free(head);
} else
lwsl_debug("%s: head %p: refcount %d: Marked as detached\n",
__func__, *head, lachead->refcount);
}

View File

@ -0,0 +1,80 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* C++ classes for Secure Streams - atomic heap messages
*/
#include <libwebsockets.hxx>
#include "private-lib-misc-lwsac.h"
void
lssAc::start(bool atomic)
{
if (atomic && ac->next) {
struct lwsac *ac2 = NULL, *i;
size_t total = (size_t)lwsac_total_alloc(ac);
uint8_t *p = (uint8_t *)lwsac_use(&ac2, total, total);
/*
* He wants a single linear buffer, and we have more than one
* piece... let's make a new, single one, copy the fragments
* in and replace the fragmented one with the unified copy.
*/
i = ac;
while (i) {
size_t bl = lwsac_get_tail_pos(i) -
lwsac_sizeof(i == ac);
memcpy(p, (uint8_t *)i + lwsac_sizeof(i == ac), bl);
p += bl;
}
lwsac_free(&ac);
ac = ac2;
}
iter = ac;
}
int
lssAc::get(lssbuf_t *lb)
{
if (!ac)
return 1;
lb->buf = (uint8_t *)iter + lwsac_sizeof(iter == ac);
lb->len = lwsac_get_tail_pos(iter) - lwsac_sizeof(iter == ac);
iter = iter->next;
return 0;
}
void
lssAc::append(lssbuf_t *lb)
{
uint8_t *p = (uint8_t *)lwsac_use(&ac, lb->len, lb->len);
if (!p)
throw lssException("oom");
memcpy(p, lb->buf, lb->len);
}

View File

@ -0,0 +1,70 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#if !defined(__LWS_PRIVATE_LIB_MISC_LWSAC_H__)
#define __LWS_PRIVATE_LIB_MISC_LWSAC_H__
#if !defined(LWS_PLAT_OPTEE)
#include <sys/stat.h>
#endif
/* under page size of 4096 to allow overhead */
#define LWSAC_CHUNK_SIZE 4000
/*
* the chunk list members all point back to the head themselves so the list
* can be detached from the formal head and free itself when its reference
* count reaches zero.
*/
/*
* One of these per chunk
*/
struct lwsac {
struct lwsac *next;
struct lwsac *head; /* pointer back to the first chunk */
size_t alloc_size; /* alloc size of the whole chunk */
size_t ofs; /* next writeable position inside chunk */
};
/*
* One of these per lwsac, at start of first chunk
*/
struct lwsac_head {
struct lwsac *curr;
size_t total_alloc_size;
int refcount;
int total_blocks;
char detached; /* if our refcount gets to zero, free the chunk list */
};
#if !defined(LWS_PLAT_OPTEE)
struct cached_file_info {
struct stat s;
time_t last_confirm;
};
#endif
#endif

View File

@ -0,0 +1,288 @@
/*
* minilex.c
*
* High efficiency lexical s parser
*
* Copyright (C)2011-2022 Andy Green <andy@warmcat.com>
*
* Licensed under MIT
*
* This is a version of the original lws http minilex that can handle ambiguous
* terminals and accepts the terminal list from stdin, producing a parsing
* table on stdout.
*
* Usage: gcc minilex.c -o minilex && \
* cat css-lextable-strings.txt | ./minilex > css-lextable.h
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* b7 = 0 = 1-byte seq
* 0x08 = fail
* 2-byte seq
* 0x00 - 0x07, then terminal as given in 2nd byte
3-byte seq
* no match: go fwd 3 byte, match: jump fwd by amt in +1/+2 bytes
* = 1 = 1-byte seq
* no match: die, match go fwd 1 byte
*/
#define PARALLEL 30
struct s {
char c[PARALLEL];
int s[PARALLEL];
int count;
int bytepos;
int real_pos;
};
struct s s[1000];
int next = 1;
#define FAIL_CHAR 0x08
int
main(void)
{
const char *rset[200];
int n = 0;
int m;
int prev;
int walk;
int y;
int j;
int pos = 0;
size_t sl = 0;
char *line = NULL;
ssize_t r;
int setmembers = 0;
memset(rset, 0, sizeof(rset));
/* Step 1: collect the strings from stdin and list in a comment */
do {
r = getline(&line, &sl, stdin);
if (r <= 0 || setmembers == sizeof(rset) / sizeof(rset[0]))
break;
if (line[r - 1] == '\n')
line[r - 1] = '\0';
printf("\t/* %d: %s */\n", setmembers, line);
rset[setmembers++] = strdup(line);
} while (1);
free(line);
/* Step 2: produce an enum template for the strings in a comment */
printf("/* enum {\n");
n = 0;
while (n < setmembers) {
char def[100];
strncpy(def, rset[n], sizeof(def));
j = 0;
while (def[j]) {
if (def[j] == '-')
def[j] = '_';
if (def[j] == ':' && !def[j + 1])
def[j] = '\0';
else
if (def[j] >= 'a' && def[j] <= 'z')
def[j] = def[j] - ('a' - 'A');
j++;
}
printf("\tXXXX_%s,\n", def);
n++;
}
printf("}; */\n\n");
/*
* Step 3: issue each character of each string into the tree, reusing
* any existing common substring subtrees
*/
n = 0;
while (n < setmembers) {
m = 0;
walk = 0;
prev = 0;
while (rset[n][m]) {
int saw = 0;
for (y = 0; y < s[walk].count; y++)
if (s[walk].c[y] == rset[n][m]) {
/* exists -- go forward */
walk = s[walk].s[y];
saw = 1;
break;
}
if (saw) {
m++;
continue;
}
/* If something we didn't see before, insert a
* conditional goto for it... however if there
* is already a terminal, we must insert the
* conditional before it. This handles
* matches on "xx" and "xxy" where "xx" is
* listed first */
s[walk].count++;
if (s[walk].count > 1 &&
!s[walk].s[s[walk].count - 2]) {
/*
* This s currently has a terminal
* at the end... insert a conditional
* behind it
*/
s[walk].c[s[walk].count - 1] =
s[walk].c[s[walk].count - 2];
s[walk].s[s[walk].count - 1] =
s[walk].s[s[walk].count - 2];
s[walk].c[s[walk].count - 2] = rset[n][m];
s[walk].s[s[walk].count - 2] = next;
} else {
/* just append a conditional */
s[walk].c[s[walk].count - 1] = rset[n][m];
s[walk].s[s[walk].count - 1] = next;
}
walk = next++;
m++;
}
/* reached the end of rset[n] */
s[walk].c[s[walk].count] = n++;
s[walk].s[s[walk].count++] = 0; /* terminal marker */
}
walk = 0;
for (n = 0; n < next; n++) {
s[n].bytepos = walk;
walk += (2 * s[n].count);
}
/* compute everyone's position first */
pos = 0;
walk = 0;
for (n = 0; n < next; n++) {
s[n].real_pos = pos;
for (m = 0; m < s[n].count; m++) {
if (s[n].s[m] == 0)
pos += 2; /* terminal marker */
else { /* c is a character */
if ((s[s[n].s[m]].bytepos -
walk) == 2)
pos++;
else {
pos += 3;
if (m == s[n].count - 1)
pos++; /* fail */
}
}
walk += 2;
}
}
walk = 0;
pos = 0;
for (n = 0; n < next; n++) {
for (m = 0; m < s[n].count; m++) {
int saw = s[n].s[m];
if (!m)
fprintf(stdout, "/* pos %04x: %3d */ ",
s[n].real_pos, n);
else
fprintf(stdout, " ");
y = s[n].c[m];
if (saw == 0) { // c is a terminal then
if (y > 0x7ff) {
fprintf(stderr, "terminal too big\n");
return 2;
}
fprintf(stdout, " 0x%02X, 0x%02X "
" "
"/* - terminal marker %2d - */,\n",
y >> 8, y & 0xff, y & 0x7f);
pos += 2;
walk += 2;
continue;
}
/* c is a character */
prev = y &0x7f;
if (prev < 32 || prev > 126)
prev = '.';
if ((s[saw].bytepos - walk) == 2) {
fprintf(stdout, " 0x%02X /* '%c' -> */,\n",
y | 0x80, prev);
pos++;
walk += 2;
continue;
}
j = s[saw].real_pos - pos;
if (j > 0xffff) {
fprintf(stderr,
"Jump > 64K bytes ahead (%d to %d)\n",
s[n].real_pos, s[saw].real_pos);
return 1;
}
fprintf(stdout, " 0x%02X /* '%c' */, 0x%02X, 0x%02X "
"/* (to 0x%04X s %3d) */,\n",
y, prev,
j & 0xff, j >> 8,
s[saw].real_pos, saw);
pos += 3;
if (m == s[n].count - 1) {
fprintf(stdout,
" 0x%02X, /* fail */\n",
FAIL_CHAR);
pos++; /* fail */
}
walk += 2;
}
}
fprintf(stdout, "/* total size %d bytes */\n", pos);
for (n = 0;n < setmembers; n++) {
free((void *)rset[n]);
rset[n] = NULL;
}
return 0;
}

View File

@ -0,0 +1,318 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <libwebsockets.h>
#include "private-lib-core.h"
/* requires context->lock */
static void
__lws_peer_remove_from_peer_wait_list(struct lws_context *context,
struct lws_peer *peer)
{
struct lws_peer *df;
lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
if (*p == peer) {
df = *p;
*p = df->peer_wait_list;
df->peer_wait_list = NULL;
if (!context->peer_wait_list)
lws_sul_cancel(&context->pt[0].sul_peer_limits);
return;
}
} lws_end_foreach_llp(p, peer_wait_list);
}
void
lws_sul_peer_limits_cb(lws_sorted_usec_list_t *sul)
{
struct lws_context_per_thread *pt = lws_container_of(sul,
struct lws_context_per_thread, sul_peer_limits);
lws_peer_cull_peer_wait_list(pt->context);
lws_sul_schedule(pt->context, 0, &pt->context->pt[0].sul_peer_limits,
lws_sul_peer_limits_cb, 10 * LWS_US_PER_SEC);
}
/* requires context->lock */
static void
__lws_peer_add_to_peer_wait_list(struct lws_context *context,
struct lws_peer *peer)
{
__lws_peer_remove_from_peer_wait_list(context, peer);
peer->peer_wait_list = context->peer_wait_list;
context->peer_wait_list = peer;
if (!context->pt[0].sul_peer_limits.list.owner)
lws_sul_schedule(context, 0, &context->pt[0].sul_peer_limits,
lws_sul_peer_limits_cb, 10 * LWS_US_PER_SEC);
}
struct lws_peer *
lws_get_or_create_peer(struct lws_vhost *vhost, lws_sockfd_type sockfd)
{
struct lws_context *context = vhost->context;
struct lws_peer *peer;
lws_sockaddr46 sa46;
socklen_t rlen = 0;
uint32_t hash = 0;
uint8_t *q8;
void *q;
int n;
if (vhost->options & LWS_SERVER_OPTION_UNIX_SOCK)
return NULL;
rlen = sizeof(sa46);
if (getpeername(sockfd, (struct sockaddr*)&sa46, &rlen))
/* eg, udp doesn't have to have a peer */
return NULL;
#ifdef LWS_WITH_IPV6
if (sa46.sa4.sin_family == AF_INET6) {
q = &sa46.sa6.sin6_addr;
rlen = sizeof(sa46.sa6.sin6_addr);
} else
#endif
{
q = &sa46.sa4.sin_addr;
rlen = sizeof(sa46.sa4.sin_addr);
}
q8 = q;
for (n = 0; n < (int)rlen; n++)
hash = (uint32_t)((((hash << 4) | (hash >> 28)) * (uint32_t)n) ^ q8[n]);
if (!context->pl_hash_elements)
return NULL;
hash = hash % context->pl_hash_elements;
lws_context_lock(context, "peer search"); /* <======================= */
lws_start_foreach_ll(struct lws_peer *, peerx,
context->pl_hash_table[hash]) {
if (peerx->sa46.sa4.sin_family == sa46.sa4.sin_family) {
#if defined(LWS_WITH_IPV6)
if (sa46.sa4.sin_family == AF_INET6 &&
!memcmp(q, &peerx->sa46.sa6.sin6_addr, rlen))
goto hit;
#endif
if (sa46.sa4.sin_family == AF_INET &&
!memcmp(q, &peerx->sa46.sa4.sin_addr, rlen)) {
#if defined(LWS_WITH_IPV6)
hit:
#endif
lws_context_unlock(context); /* === */
return peerx;
}
}
} lws_end_foreach_ll(peerx, next);
lwsl_info("%s: creating new peer\n", __func__);
peer = lws_zalloc(sizeof(*peer), "peer");
if (!peer) {
lws_context_unlock(context); /* === */
lwsl_err("%s: OOM for new peer\n", __func__);
return NULL;
}
context->count_peers++;
peer->next = context->pl_hash_table[hash];
peer->hash = hash;
peer->sa46 = sa46;
context->pl_hash_table[hash] = peer;
time(&peer->time_created);
/*
* On creation, the peer has no wsi attached, so is created on the
* wait list. When a wsi is added it is removed from the wait list.
*/
time(&peer->time_closed_all);
__lws_peer_add_to_peer_wait_list(context, peer);
lws_context_unlock(context); /* ====================================> */
return peer;
}
/* requires context->lock */
static int
__lws_peer_destroy(struct lws_context *context, struct lws_peer *peer)
{
lws_start_foreach_llp(struct lws_peer **, p,
context->pl_hash_table[peer->hash]) {
if (*p == peer) {
struct lws_peer *df = *p;
*p = df->next;
lws_free(df);
context->count_peers--;
return 0;
}
} lws_end_foreach_llp(p, next);
return 1;
}
void
lws_peer_cull_peer_wait_list(struct lws_context *context)
{
struct lws_peer *df;
time_t t;
time(&t);
if (context->next_cull && t < context->next_cull)
return;
lws_context_lock(context, "peer cull"); /* <========================= */
context->next_cull = t + 5;
lws_start_foreach_llp(struct lws_peer **, p, context->peer_wait_list) {
if (t - (*p)->time_closed_all > 10) {
df = *p;
/* remove us from the peer wait list */
*p = df->peer_wait_list;
df->peer_wait_list = NULL;
__lws_peer_destroy(context, df);
continue; /* we already point to next, if any */
}
} lws_end_foreach_llp(p, peer_wait_list);
lws_context_unlock(context); /* ====================================> */
}
void
lws_peer_add_wsi(struct lws_context *context, struct lws_peer *peer,
struct lws *wsi)
{
if (!peer)
return;
lws_context_lock(context, "peer add"); /* <========================== */
peer->count_wsi++;
wsi->peer = peer;
__lws_peer_remove_from_peer_wait_list(context, peer);
lws_context_unlock(context); /* ====================================> */
}
void
lws_peer_dump_from_wsi(struct lws *wsi)
{
struct lws_peer *peer;
if (!wsi || !wsi->peer)
return;
peer = wsi->peer;
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
lwsl_notice("%s: %s: created %llu: wsi: %d/%d, ah %d/%d\n",
__func__, lws_wsi_tag(wsi),
(unsigned long long)peer->time_created,
peer->count_wsi, peer->total_wsi,
peer->http.count_ah, peer->http.total_ah);
#else
lwsl_notice("%s: %s: created %llu: wsi: %d/%d\n", __func__,
lws_wsi_tag(wsi),
(unsigned long long)peer->time_created,
peer->count_wsi, peer->total_wsi);
#endif
}
void
lws_peer_track_wsi_close(struct lws_context *context, struct lws_peer *peer)
{
if (!peer)
return;
lws_context_lock(context, "peer wsi close"); /* <==================== */
assert(peer->count_wsi);
peer->count_wsi--;
if (!peer->count_wsi
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
&& !peer->http.count_ah
#endif
) {
/*
* in order that we can accumulate peer activity correctly
* allowing for periods when the peer has no connections,
* we don't synchronously destroy the peer when his last
* wsi closes. Instead we mark the time his last wsi
* closed and add him to a peer_wait_list to be reaped
* later if no further activity is coming.
*/
time(&peer->time_closed_all);
__lws_peer_add_to_peer_wait_list(context, peer);
}
lws_context_unlock(context); /* ====================================> */
}
#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)
int
lws_peer_confirm_ah_attach_ok(struct lws_context *context,
struct lws_peer *peer)
{
if (!peer)
return 0;
if (context->ip_limit_ah &&
peer->http.count_ah >= context->ip_limit_ah) {
lwsl_info("peer reached ah limit %d, deferring\n",
context->ip_limit_ah);
return 1;
}
return 0;
}
void
lws_peer_track_ah_detach(struct lws_context *context, struct lws_peer *peer)
{
if (!peer)
return;
lws_context_lock(context, "peer ah detach"); /* <==================== */
assert(peer->http.count_ah);
peer->http.count_ah--;
lws_context_unlock(context); /* ====================================> */
}
#endif

View File

@ -0,0 +1,80 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* After Public Domain implementations
*
* https://github.com/svaarala/duktape/tree/master/misc
*/
#include <private-lib-core.h>
static inline uint64_t rol64(uint64_t x, int k)
{
return (x << k) | (x >> (64 - k));
}
uint64_t
lws_xos(struct lws_xos *xos)
{
uint64_t *s = &xos->s[0];
uint64_t const result = rol64(s[1] * 5, 7) * 9;
uint64_t const c = s[1] << 17;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= c;
s[3] = rol64(s[3], 45);
return result;
}
static uint64_t
splitmix64(uint64_t *s)
{
uint64_t r = *s;
*s = r + 0x9E3779B97F4A7C15ull;
r = (r ^ (r >> 30)) * 0xBF58476D1CE4E5B9ull;
r = (r ^ (r >> 27)) * 0x94D049BB133111EBull;
return r ^ (r >> 31);
}
void
lws_xos_init(struct lws_xos *xos, uint64_t seed)
{
int n;
for (n = 0; n < 4; n++)
xos->s[n] = splitmix64(&seed);
}
int
lws_xos_percent(struct lws_xos *xos, int percent)
{
return (int)(lws_xos(xos) % 100) < percent;
}

View File

@ -0,0 +1,241 @@
/*
* Copyright (C) 2017 National Institute of Advanced Industrial Science
* and Technology (AIST)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of AIST nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <libwebsockets.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include "romfs.h"
#if defined(LWS_WITH_ESP32)
#include <esp_flash.h>
#include "spi_flash_mmap.h"
#endif
#define RFS_STRING_MAX 96
static u32_be_t cache[(RFS_STRING_MAX + 32) / 4];
static romfs_inode_t ci = (romfs_inode_t)cache;
static romfs_t cr = (romfs_t)cache;
static void
set_cache(romfs_inode_t inode, size_t len)
{
#if defined(LWS_WITH_ESP32)
// spi_flash_read((uint32_t)inode, cache, len);
// esp_err_t esp_flash_read(esp_flash_t *chip, void *buffer, uint32_t address, uint32_t length);
esp_flash_read(NULL, (void *)inode, (uint32_t)cache, len);
#endif
}
static uint32_t
untohl(const u32_be_t be)
{
return ((be >> 24) & 0xff) |
((be >> 16) & 0xff) << 8 |
((be >> 8) & 0xff) << 16 |
(be & 0xff) << 24;
}
static romfs_inode_t
romfs_lookup(romfs_t romfs, romfs_inode_t start, const char *path);
static int
plus_padding(const uint8_t *s)
{
int n;
set_cache((romfs_inode_t)s, RFS_STRING_MAX);
n = strlen((const char *)cache);
if (!(n & 15))
n += 0x10;
return (n + 15) & ~15;
}
static romfs_inode_t
skip_and_pad(romfs_inode_t ri)
{
const uint8_t *p = ((const uint8_t *)ri) + sizeof(*ri);
return (romfs_inode_t)(p + plus_padding(p));
}
size_t
romfs_mount_check(romfs_t romfs)
{
set_cache((romfs_inode_t)romfs, sizeof(*romfs));
if (cr->magic1 != 0x6d6f722d ||
cr->magic2 != 0x2d736631)
return 0;
return untohl(cr->size);
}
static romfs_inode_t
romfs_symlink(romfs_t romfs, romfs_inode_t level, romfs_inode_t i)
{
const char *p = (const char *)skip_and_pad(i);
if (*p == '/') {
level = skip_and_pad((romfs_inode_t)romfs);
p++;
}
return romfs_lookup(romfs, level, p);
}
static romfs_inode_t
dir_link(romfs_t romfs, romfs_inode_t i)
{
set_cache(i, sizeof(*i));
return (romfs_inode_t)((const uint8_t *)romfs +
untohl(ci->dir_start));
}
static romfs_inode_t
romfs_lookup(romfs_t romfs, romfs_inode_t start, const char *path)
{
romfs_inode_t level, i = start, i_in;
const char *p, *cp;
uint32_t next_be;
if (start == (romfs_inode_t)romfs)
i = skip_and_pad((romfs_inode_t)romfs);
level = i;
while (i != (romfs_inode_t)romfs) {
const char *n = ((const char *)i) + sizeof(*i);
p = path;
i_in = i;
set_cache(i, sizeof(*i));
next_be = ci->next;
cp = (const char *)cache;
set_cache((romfs_inode_t)n, RFS_STRING_MAX);
while (*p && *p != '/' && *cp && *p == *cp &&
(p - path) < RFS_STRING_MAX) {
p++;
n++;
cp++;
}
while (*p == '/' && p[1] == '/')
p++;
if (!*cp && (!*p || *p == '/') &&
(untohl(next_be) & 7) == RFST_HARDLINK) {
set_cache(i, sizeof(*i));
return (romfs_inode_t)
((const uint8_t *)romfs +
(untohl(ci->dir_start) & ~15));
}
if (!*p && !*cp) {
set_cache(i, sizeof(*i));
if ((untohl(ci->next) & 7) == RFST_SYMLINK) {
i = romfs_symlink(romfs, level, i);
continue;
}
return i;
}
if (!*p && *cp == '/')
return NULL;
while (*p == '/' && p[1] == '/')
p++;
if (*p == '/' && !*cp) {
set_cache(i, sizeof(*i));
switch (untohl(ci->next) & 7) {
case RFST_SYMLINK:
i = romfs_symlink(romfs, level, i);
if (!i)
return NULL;
i = dir_link(romfs, i);
while (*path != '/' && *path)
path++;
if (!*path)
return NULL;
path++;
continue;
case RFST_DIR:
path = p + 1;
i = dir_link(romfs, i);
break;
default:
path = p + 1;
i = skip_and_pad(i);
break;
}
level = i;
continue;
}
set_cache(i, sizeof(*i));
if (!(untohl(ci->next) & ~15))
return NULL;
i = (romfs_inode_t)((const uint8_t *)romfs +
(untohl(ci->next) & ~15));
if (i == i_in)
return NULL;
}
return NULL;
}
const void *
romfs_get_info(romfs_t romfs, const char *path, size_t *len, size_t *csum)
{
romfs_inode_t i;
if (*path == '/')
path++;
i = romfs_lookup(romfs, (romfs_inode_t)romfs, path);
if (!i)
return NULL;
set_cache(i, sizeof(*i));
*len = untohl(ci->size);
if (csum)
*csum = untohl(ci->checksum);
return (void *)skip_and_pad(i);
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2017 National Institute of Advanced Industrial Science
* and Technology (AIST)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of AIST nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
typedef uint32_t u32_be_t;
struct romfs_superblock {
u32_be_t magic1;
u32_be_t magic2;
u32_be_t size;
u32_be_t checksum;
};
struct romfs_i {
u32_be_t next;
u32_be_t dir_start;
u32_be_t size;
u32_be_t checksum;
};
enum {
RFST_HARDLINK = 0,
RFST_DIR = 1,
RFST_SYMLINK = 3,
};
typedef const struct romfs_i *romfs_inode_t;
typedef const struct romfs_superblock *romfs_t;
const void *
romfs_get_info(romfs_t romfs, const char *path, size_t *len, size_t *csum);
size_t
romfs_mount_check(romfs_t romfs);

View File

@ -0,0 +1,296 @@
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* FIPS pub 180-1: Secure Hash Algorithm (SHA-1)
* based on: http://csrc.nist.gov/fips/fip180-1.txt
* implemented by Jun-ichiro itojun Itoh <itojun@itojun.org>
*/
#include "private-lib-core.h"
#ifdef LWS_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
struct sha1_ctxt {
union {
unsigned char b8[20];
unsigned int b32[5];
} h;
union {
unsigned char b8[8];
uint64_t b64[1];
} c;
union {
unsigned char b8[64];
unsigned int b32[16];
} m;
unsigned char count;
};
/* sanity check */
#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN)
# define unsupported 1
#elif BYTE_ORDER != BIG_ENDIAN
# if BYTE_ORDER != LITTLE_ENDIAN
# define unsupported 1
# endif
#endif
#ifndef unsupported
/* constant table */
static const unsigned int _K[] =
{ 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 };
#define K(t) _K[(t) / 20]
#define F0(b, c, d) (((b) & (c)) | ((~(b)) & (d)))
#define F1(b, c, d) (((b) ^ (c)) ^ (d))
#define F2(b, c, d) (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
#define F3(b, c, d) (((b) ^ (c)) ^ (d))
#define S(n, x) (((x) << (n)) | ((x) >> (32 - n)))
#define H(n) (ctxt->h.b32[(n)])
#define COUNT (ctxt->count)
#define BCOUNT (ctxt->c.b64[0] / 8)
#define W(n) (ctxt->m.b32[(n)])
#define PUTBYTE(x) { \
ctxt->m.b8[(COUNT % 64)] = (x); \
COUNT++; \
COUNT %= 64; \
ctxt->c.b64[0] += 8; \
if (COUNT % 64 == 0) \
sha1_step(ctxt); \
}
#define PUTPAD(x) { \
ctxt->m.b8[(COUNT % 64)] = (x); \
COUNT++; \
COUNT %= 64; \
if (COUNT % 64 == 0) \
sha1_step(ctxt); \
}
static void
sha1_step(struct sha1_ctxt *ctxt)
{
unsigned int a, b, c, d, e, tmp;
size_t t, s;
#if BYTE_ORDER == LITTLE_ENDIAN
struct sha1_ctxt tctxt;
memcpy(&tctxt.m.b8[0], &ctxt->m.b8[0], 64);
ctxt->m.b8[0] = tctxt.m.b8[3]; ctxt->m.b8[1] = tctxt.m.b8[2];
ctxt->m.b8[2] = tctxt.m.b8[1]; ctxt->m.b8[3] = tctxt.m.b8[0];
ctxt->m.b8[4] = tctxt.m.b8[7]; ctxt->m.b8[5] = tctxt.m.b8[6];
ctxt->m.b8[6] = tctxt.m.b8[5]; ctxt->m.b8[7] = tctxt.m.b8[4];
ctxt->m.b8[8] = tctxt.m.b8[11]; ctxt->m.b8[9] = tctxt.m.b8[10];
ctxt->m.b8[10] = tctxt.m.b8[9]; ctxt->m.b8[11] = tctxt.m.b8[8];
ctxt->m.b8[12] = tctxt.m.b8[15]; ctxt->m.b8[13] = tctxt.m.b8[14];
ctxt->m.b8[14] = tctxt.m.b8[13]; ctxt->m.b8[15] = tctxt.m.b8[12];
ctxt->m.b8[16] = tctxt.m.b8[19]; ctxt->m.b8[17] = tctxt.m.b8[18];
ctxt->m.b8[18] = tctxt.m.b8[17]; ctxt->m.b8[19] = tctxt.m.b8[16];
ctxt->m.b8[20] = tctxt.m.b8[23]; ctxt->m.b8[21] = tctxt.m.b8[22];
ctxt->m.b8[22] = tctxt.m.b8[21]; ctxt->m.b8[23] = tctxt.m.b8[20];
ctxt->m.b8[24] = tctxt.m.b8[27]; ctxt->m.b8[25] = tctxt.m.b8[26];
ctxt->m.b8[26] = tctxt.m.b8[25]; ctxt->m.b8[27] = tctxt.m.b8[24];
ctxt->m.b8[28] = tctxt.m.b8[31]; ctxt->m.b8[29] = tctxt.m.b8[30];
ctxt->m.b8[30] = tctxt.m.b8[29]; ctxt->m.b8[31] = tctxt.m.b8[28];
ctxt->m.b8[32] = tctxt.m.b8[35]; ctxt->m.b8[33] = tctxt.m.b8[34];
ctxt->m.b8[34] = tctxt.m.b8[33]; ctxt->m.b8[35] = tctxt.m.b8[32];
ctxt->m.b8[36] = tctxt.m.b8[39]; ctxt->m.b8[37] = tctxt.m.b8[38];
ctxt->m.b8[38] = tctxt.m.b8[37]; ctxt->m.b8[39] = tctxt.m.b8[36];
ctxt->m.b8[40] = tctxt.m.b8[43]; ctxt->m.b8[41] = tctxt.m.b8[42];
ctxt->m.b8[42] = tctxt.m.b8[41]; ctxt->m.b8[43] = tctxt.m.b8[40];
ctxt->m.b8[44] = tctxt.m.b8[47]; ctxt->m.b8[45] = tctxt.m.b8[46];
ctxt->m.b8[46] = tctxt.m.b8[45]; ctxt->m.b8[47] = tctxt.m.b8[44];
ctxt->m.b8[48] = tctxt.m.b8[51]; ctxt->m.b8[49] = tctxt.m.b8[50];
ctxt->m.b8[50] = tctxt.m.b8[49]; ctxt->m.b8[51] = tctxt.m.b8[48];
ctxt->m.b8[52] = tctxt.m.b8[55]; ctxt->m.b8[53] = tctxt.m.b8[54];
ctxt->m.b8[54] = tctxt.m.b8[53]; ctxt->m.b8[55] = tctxt.m.b8[52];
ctxt->m.b8[56] = tctxt.m.b8[59]; ctxt->m.b8[57] = tctxt.m.b8[58];
ctxt->m.b8[58] = tctxt.m.b8[57]; ctxt->m.b8[59] = tctxt.m.b8[56];
ctxt->m.b8[60] = tctxt.m.b8[63]; ctxt->m.b8[61] = tctxt.m.b8[62];
ctxt->m.b8[62] = tctxt.m.b8[61]; ctxt->m.b8[63] = tctxt.m.b8[60];
#endif
a = H(0); b = H(1); c = H(2); d = H(3); e = H(4);
for (t = 0; t < 20; t++) {
s = t & 0x0f;
if (t >= 16)
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F0(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 20; t < 40; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F1(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 40; t < 60; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F2(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
for (t = 60; t < 80; t++) {
s = t & 0x0f;
W(s) = S(1, W((s+13) & 0x0f) ^ W((s+8) & 0x0f) ^
W((s+2) & 0x0f) ^ W(s));
tmp = S(5, a) + F3(b, c, d) + e + W(s) + K(t);
e = d; d = c; c = S(30, b); b = a; a = tmp;
}
H(0) = H(0) + a;
H(1) = H(1) + b;
H(2) = H(2) + c;
H(3) = H(3) + d;
H(4) = H(4) + e;
memset(&ctxt->m.b8[0], 0, 64);
}
/*------------------------------------------------------------*/
static void
_sha1_init(struct sha1_ctxt *ctxt)
{
memset(ctxt, 0, sizeof(struct sha1_ctxt));
H(0) = 0x67452301;
H(1) = 0xefcdab89;
H(2) = 0x98badcfe;
H(3) = 0x10325476;
H(4) = 0xc3d2e1f0;
}
void
sha1_pad(struct sha1_ctxt *ctxt)
{
size_t padlen; /*pad length in bytes*/
size_t padstart;
PUTPAD(0x80);
padstart = COUNT % 64;
padlen = 64 - padstart;
if (padlen < 8) {
memset(&ctxt->m.b8[padstart], 0, padlen);
COUNT = (unsigned char)(COUNT + padlen);
COUNT %= 64;
sha1_step(ctxt);
padstart = COUNT % 64; /* should be 0 */
padlen = 64 - padstart; /* should be 64 */
}
memset(&ctxt->m.b8[padstart], 0, padlen - 8);
COUNT = (unsigned char)(COUNT + (padlen - 8));
COUNT %= 64;
#if BYTE_ORDER == BIG_ENDIAN
PUTPAD(ctxt->c.b8[0]); PUTPAD(ctxt->c.b8[1]);
PUTPAD(ctxt->c.b8[2]); PUTPAD(ctxt->c.b8[3]);
PUTPAD(ctxt->c.b8[4]); PUTPAD(ctxt->c.b8[5]);
PUTPAD(ctxt->c.b8[6]); PUTPAD(ctxt->c.b8[7]);
#else
PUTPAD(ctxt->c.b8[7]); PUTPAD(ctxt->c.b8[6]);
PUTPAD(ctxt->c.b8[5]); PUTPAD(ctxt->c.b8[4]);
PUTPAD(ctxt->c.b8[3]); PUTPAD(ctxt->c.b8[2]);
PUTPAD(ctxt->c.b8[1]); PUTPAD(ctxt->c.b8[0]);
#endif
}
void
sha1_loop(struct sha1_ctxt *ctxt, const unsigned char *input, size_t len)
{
size_t off;
off = 0;
while (off < len) {
size_t gapstart = COUNT % 64, gaplen = 64 - gapstart,
copysiz = (gaplen < len - off) ? gaplen : len - off;
memcpy(&ctxt->m.b8[gapstart], &input[off], copysiz);
COUNT = (unsigned char)(COUNT + copysiz);
COUNT %= 64;
ctxt->c.b64[0] += copysiz * 8;
if (COUNT % 64 == 0)
sha1_step(ctxt);
off += copysiz;
}
}
void
sha1_result(struct sha1_ctxt *ctxt, void *digest0)
{
unsigned char *digest;
digest = (unsigned char *)digest0;
sha1_pad(ctxt);
#if BYTE_ORDER == BIG_ENDIAN
memcpy(digest, &ctxt->h.b8[0], 20);
#else
digest[0] = ctxt->h.b8[3]; digest[1] = ctxt->h.b8[2];
digest[2] = ctxt->h.b8[1]; digest[3] = ctxt->h.b8[0];
digest[4] = ctxt->h.b8[7]; digest[5] = ctxt->h.b8[6];
digest[6] = ctxt->h.b8[5]; digest[7] = ctxt->h.b8[4];
digest[8] = ctxt->h.b8[11]; digest[9] = ctxt->h.b8[10];
digest[10] = ctxt->h.b8[9]; digest[11] = ctxt->h.b8[8];
digest[12] = ctxt->h.b8[15]; digest[13] = ctxt->h.b8[14];
digest[14] = ctxt->h.b8[13]; digest[15] = ctxt->h.b8[12];
digest[16] = ctxt->h.b8[19]; digest[17] = ctxt->h.b8[18];
digest[18] = ctxt->h.b8[17]; digest[19] = ctxt->h.b8[16];
#endif
}
/*
* This should look and work like the libcrypto implementation
*/
unsigned char *
lws_SHA1(const unsigned char *d, size_t n, unsigned char *md)
{
struct sha1_ctxt ctx;
_sha1_init(&ctx);
sha1_loop(&ctx, d, n);
sha1_result(&ctx, (void *)md);
return md;
}
#endif /*unsupported*/

View File

@ -0,0 +1,182 @@
## Threadpool
### Overview
![overview](/doc-assets/threadpool.svg)
An api that lets you create a pool of worker threads, and a queue of tasks that
are bound to a wsi. Tasks in their own thread synchronize communication to the
lws service thread of the wsi via `LWS_CALLBACK_SERVER_WRITEABLE` and friends.
Tasks can produce some output, then return that they want to "sync" with the
service thread. That causes a `LWS_CALLBACK_SERVER_WRITEABLE` in the service
thread context, where the output can be consumed, and the task told to continue,
or completed tasks be reaped.
ALL of the details related to thread synchronization and an associated wsi in
the lws service thread context are handled by the threadpool api, without needing
any pthreads in user code.
### Example
https://libwebsockets.org/git/libwebsockets/tree/minimal-examples/ws-server/minimal-ws-server-threadpool
### Lifecycle considerations
#### Tasks vs wsi
Although all tasks start out as being associated to a wsi, in fact the lifetime
of a task and that of the wsi are not necessarily linked.
You may start a long task, eg, that runs atomically in its thread for 30s, and
at any time the client may close the connection, eg, close a browser window.
There are arrangements that a task can "check in" periodically with lws to see
if it has been asked to stop, allowing the task lifetime to be related to the
wsi lifetime somewhat, but some tasks are going to be atomic and longlived.
For that reason, at wsi close an ongoing task can detach from the wsi and
continue until it ends or understands it has been asked to stop. To make
that work, the task is created with a `cleanup` callback that performs any
freeing independent of still having a wsi around to do it... the task takes over
responsibility to free the user pointer on destruction when the task is created.
![Threadpool States](/doc-assets/threadpool-states.svg)
#### Reaping completed tasks
Once created, although tasks may run asynchronously, the task itself does not
get destroyed on completion but added to a "done queue". Only when the lws
service thread context queries the task state with `lws_threadpool_task_status()`
may the task be reaped and memory freed.
This is analogous to unix processes and `wait()`.
If a task became detached from its wsi, then joining the done queue is enough
to get the task reaped, since there's nobody left any more to synchronize the
reaping with.
### User interface
The api is declared at https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-threadpool.h
#### Threadpool creation / destruction
The threadpool should be created at program or vhost init using
`lws_threadpool_create()` and destroyed on exit or vhost destruction using
first `lws_threadpool_finish()` and then `lws_threadpool_destroy()`.
Threadpools should be named, varargs are provided on the create function
to facilite eg, naming the threadpool by the vhost it's associated with.
Threadpool creation takes an args struct with the following members:
Member|function
---|---
threads|The maxiumum number of independent threads in the pool
max_queue_depth|The maximum number of tasks allowed to wait for a place in the pool
#### Task creation / destruction
Tasks are created and queued using `lws_threadpool_enqueue()`, this takes an
args struct with the following members
Member|function
---|---
wsi|The wsi the task is initially associated with
user|An opaque user-private pointer used for communication with the lws service thread and private state / data
task|A pointer to the function that will run in the pool thread
cleanup|A pointer to a function that will clean up finished or stopped tasks (perhaps freeing user)
Tasks also should have a name, the creation function again provides varargs
to simplify naming the task with string elements related to who started it
and why.
#### The task function itself
The task function receives the task user pointer and the task state. The
possible task states are
State|Meaning
---|---
LWS_TP_STATUS_QUEUED|Task is still waiting for a pool thread
LWS_TP_STATUS_RUNNING|Task is supposed to do its work
LWS_TP_STATUS_SYNCING|Task is blocked waiting for sync from lws service thread
LWS_TP_STATUS_STOPPING|Task has been asked to stop but didn't stop yet
LWS_TP_STATUS_FINISHED|Task has reported it has completed
LWS_TP_STATUS_STOPPED|Task has aborted
The task function will only be told `LWS_TP_STATUS_RUNNING` or
`LWS_TP_STATUS_STOPPING` in its status argument... RUNNING means continue with the
user task and STOPPING means clean up and return `LWS_TP_RETURN_STOPPED`.
If possible every 100ms or so the task should return `LWS_TP_RETURN_CHECKING_IN`
to allow lws to inform it reasonably quickly that it has been asked to stop
(eg, because the related wsi has closed), or if it can continue. If not
possible, it's okay but eg exiting the application may experience delays
until the running task finishes, and since the wsi may have gone, the work
is wasted.
The task function may return one of
Return|Meaning
---|---
LWS_TP_RETURN_CHECKING_IN|Still wants to run, but confirming nobody asked him to stop. Will be called again immediately with `LWS_TP_STATUS_RUNNING` or `LWS_TP_STATUS_STOPPING`
LWS_TP_RETURN_SYNC|Task wants to trigger a WRITABLE callback and block until lws service thread restarts it with `lws_threadpool_task_sync()`
LWS_TP_RETURN_FINISHED|Task has finished, successfully as far as it goes
LWS_TP_RETURN_STOPPED|Task has finished, aborting in response to a request to stop
The SYNC or CHECKING_IN return may also have a flag `LWS_TP_RETURN_FLAG_OUTLIVE`
applied to it, which indicates to threadpool that this task wishes to remain
unstopped after the wsi closes. This is useful in the case where the task
understands it will take a long time to complete, and wants to return a
complete status and maybe close the connection, perhaps with a token identifying
the task. The task can then be monitored separately by using the token.
#### Synchronizing
The task can choose to "SYNC" with the lws service thread, in other words
cause a WRITABLE callback on the associated wsi in the lws service thread
context and block itself until it hears back from there via
`lws_threadpool_task_sync()` to resume the task.
This is typically used when, eg, the task has filled its buffer, or ringbuffer,
and needs to pause operations until what's done has been sent and some buffer
space is open again.
In the WRITABLE callback, in lws service thread context, the buffer can be
sent with `lws_write()` and then `lws_threadpool_task_sync()` to allow the task
to fill another buffer and continue that way.
If the WRITABLE callback determines that the task should stop, it can just call
`lws_threadpool_task_sync()` with the second argument as 1, to force the task
to stop immediately after it resumes.
#### The cleanup function
When a finished task is reaped, or a task that become detached from its initial
wsi completes or is stopped, it calls the `.cleanup` function defined in the
task creation args struct to free anything related to the user pointer.
With threadpool, responsibility for freeing allocations used by the task belongs
strictly with the task, via the `.cleanup` function, once the task has been
enqueued. That's different from a typical non-threadpool protocol where the
wsi lifecycle controls deallocation. This reflects the fact that the task
may outlive the wsi.
#### Protecting against WRITABLE and / or SYNC duplication
Care should be taken than data prepared by the task thread in the user priv
memory should only be sent once. For example, after sending data from a user
priv buffer of a given length stored in the priv, zero down the length.
Task execution and the SYNC writable callbacks are mutually exclusive, so there
is no danger of collision between the task thread and the lws service thread if
the reason for the callback is a SYNC operation from the task thread.
### Thread overcommit
If the tasks running on the threads are ultimately network-bound for all or some
of their processing (via the SYNC with the WRITEABLE callback), it's possible
to overcommit the number of threads in the pool compared to the number of
threads the processor has in hardware to get better occupancy in the CPU.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,726 @@
/*
* LWS PNG -- derived from uPNG -- derived from LodePNG version 20100808
* Stateful, linewise PNG decode requiring ~36KB fixed heap
*
* Copyright (c) 2005-2010 Lode Vandevenne (LodePNG)
* Copyright (c) 2010 Sean Middleditch (uPNG)
* Copyright (c) 2021 Andy Green <andy@warmcat.com> (Stateful, incremental)
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* AG: The above notice is the ZLIB license, libpng also uses it.
*
* This version was rewritten from the upng project's fork of lodepng and
* adapted to be a stateful stream parser. This rewrite retains the ZLIB
* license of the source material for simplicity.
*
* That allows it to use a fixed 32KB ringbuffer to hold decodes, and
* incrementally decode chunks into it as we want output lines that are not yet
* present there. The input png nor the output bitmap need to be all in one
* place at one time.
*/
#include <private-lib-core.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
typedef enum upng_color {
LWS_UPNG_LUM = 0,
LWS_UPNG_RGB = 2,
LWS_UPNG_LUMA = 4,
LWS_UPNG_RGBA = 6
} upng_color;
struct upng_unfline {
uint8_t *recon;
const uint8_t *scanline;
const uint8_t *precon;
uint8_t filterType;
unsigned int bypp;
unsigned int bypl;
const uint8_t *in;
uint8_t *lines;
unsigned int bpp;
unsigned int y;
unsigned long diff;
unsigned long ibp;
unsigned long sp;
char padded;
char alt;
};
typedef enum {
UOF_MAGIC,
UOF_SKIP,
UOF_TYPE4,
UOF_WIDTH4,
UOF_HEIGHT4,
UOF_CDEPTH,
UOF_CTYPE,
UOF_ONLY_ZERO3,
UOF_SKIP4,
UOF_CHUNK_LEN,
UOF_CHUNK_TYPE,
UOF_INSIDE,
UOF_SKIP_CHUNK_LEN,
} upng_outer_framing_t;
struct lws_upng_t {
struct upng_unfline u;
inflator_ctx_t inf;
unsigned int width;
unsigned int height;
upng_color color_type;
unsigned int color_depth;
lws_upng_format_t format;
const uint8_t *chunk;
int sctr;
uint32_t acc;
uint32_t chunklen;
uint32_t ctype;
upng_outer_framing_t of;
uint8_t no_more_input;
char hold_at_metadata;
};
static lws_stateful_ret_t
lws_upng_decode(lws_upng_t *upng, const uint8_t **buf, size_t *size);
static int
paeth(int a, int b, int c)
{
int p = a + b - c;
int pa = p > a ? p - a : a - p;
int pb = p > b ? p - b : b - p;
int pc = p > c ? p - c : c - p;
if (pa <= pb && pa <= pc)
return a;
if (pb <= pc)
return b;
return c;
}
static lws_stateful_ret_t
unfilter_scanline(lws_upng_t *u)
{
struct upng_unfline *uf = &u->u;
unsigned long i;
switch (uf->filterType) {
case 0: /* None */
for (i = 0; i < uf->bypl; i++)
uf->recon[i] = u->inf.out[(uf->sp + i) %
u->inf.info_size];
break;
case 1: /* Sub */
for (i = 0; i < uf->bypp; i++)
uf->recon[i] = u->inf.out[(uf->sp + i) %
u->inf.info_size];
for (i = uf->bypp; i < uf->bypl; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size] +
uf->recon[i - uf->bypp]);
break;
case 2: /* Up */
if (uf->y)
for (i = 0; i < uf->bypl; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size] + uf->precon[i]);
else
for (i = 0; i < uf->bypl; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size]);
break;
case 3: /* Average */
if (uf->y) {
for (i = 0; i < uf->bypp; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size] + uf->precon[i] / 2);
for (i = uf->bypp; i < uf->bypl; i++)
uf->recon[i] = (uint8_t)
(u->inf.out[(uf->sp + i) %
u->inf.info_size] +
((uf->recon[i - uf->bypp] +
uf->precon[i]) / 2));
} else {
for (i = 0; i < uf->bypp; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size]);
for (i = uf->bypp; i < uf->bypl; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size] +
uf->recon[i - uf->bypp] / 2);
}
break;
case 4: /* Paeth */
if (uf->y) {
for (i = 0; i < uf->bypp; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size] +
paeth(0, uf->precon[i], 0));
for (i = uf->bypp; i < uf->bypl; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size] +
paeth(uf->recon[i - uf->bypp],
uf->precon[i],
uf->precon[i - uf->bypp]));
break;
}
for (i = 0; i < uf->bypp; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size]);
for (i = uf->bypp; i < uf->bypl; i++)
uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
u->inf.info_size] +
paeth(uf->recon[i - uf->bypp], 0, 0));
break;
default:
lwsl_err("%s: line start is broken %d\n", __func__,
uf->filterType);
return LWS_SRET_FATAL + 12;
}
u->inf.consumed_linear += uf->bypl;
return LWS_SRET_OK;
}
lws_stateful_ret_t
lws_upng_emit_next_line(lws_upng_t *u, const uint8_t **ppix,
const uint8_t **pos, size_t *size, char hold_at_metadata)
{
struct upng_unfline *uf = &u->u;
unsigned long obp;
lws_stateful_ret_t ret = LWS_SRET_OK;
*ppix = NULL;
u->hold_at_metadata = hold_at_metadata;
if (u->height && uf->y >= u->height)
goto out;
/*
* The decoder emits into the 32KB window ringbuffer, if we don't
* already have at least one line's worth of output in there, we'll
* have to do more inflation
*/
if (u->inf.outpos_linear - u->inf.consumed_linear < uf->bypl + 1) {
ret = lws_upng_decode(u, pos, size);
if ((!*size && ret == LWS_SRET_WANT_INPUT) ||
(ret & (LWS_SRET_FATAL | LWS_SRET_YIELD)) ||
!u->inf.outpos_linear)
return ret;
assert(u->inf.info_size);
assert(uf->bypl + 1);
}
if (u->inf.outpos_linear - u->inf.consumed_linear < uf->bypl + 1)
return ret;
obp = uf->alt ? uf->bypl : 0;
uf->precon = uf->alt ? uf->lines : uf->lines + uf->bypl;
uf->recon = &uf->lines[obp];
*ppix = uf->recon;
uf->filterType = uf->in[(u->inf.consumed_linear++) % u->inf.info_size];
uf->sp = u->inf.consumed_linear % u->inf.info_size;
if (unfilter_scanline(u) != LWS_SRET_OK) {
ret = LWS_SRET_FATAL + 13;
goto out;
}
if (uf->padded) {
unsigned long x;
for (x = 0; x < (unsigned long)u->width * (unsigned long)uf->bpp; x++) {
uint8_t bit = (uint8_t)((uf->in[(uf->ibp) >> 3] >>
(7 - ((uf->ibp) & 7))) & 1);
uf->ibp++;
if (!bit)
uf->lines[obp >> 3] &=
(uint8_t)(~(1 << (7 - (obp & 7))));
else
uf->lines[obp >> 3] = (uint8_t)(uf->lines[obp >> 3] |
(uint8_t)(1 << (7 - (obp & 7))));
obp++;
}
uf->ibp += uf->diff;
}
out:
uf->alt ^= 1;
uf->y++;
return ret;
}
static lws_upng_format_t
determine_format(lws_upng_t* upng) {
switch (upng->color_type) {
case LWS_UPNG_LUM:
switch (upng->color_depth) {
case 1:
return LWS_UPNG_LUMINANCE1;
case 2:
return LWS_UPNG_LUMINANCE2;
case 4:
return LWS_UPNG_LUMINANCE4;
case 8:
return LWS_UPNG_LUMINANCE8;
default:
return LWS_UPNG_BADFORMAT;
}
case LWS_UPNG_RGB:
switch (upng->color_depth) {
case 8:
return LWS_UPNG_RGB8;
case 16:
return LWS_UPNG_RGB16;
default:
return LWS_UPNG_BADFORMAT;
}
case LWS_UPNG_LUMA:
switch (upng->color_depth) {
case 1:
return LWS_UPNG_LUMINANCE_ALPHA1;
case 2:
return LWS_UPNG_LUMINANCE_ALPHA2;
case 4:
return LWS_UPNG_LUMINANCE_ALPHA4;
case 8:
return LWS_UPNG_LUMINANCE_ALPHA8;
default:
return LWS_UPNG_BADFORMAT;
}
case LWS_UPNG_RGBA:
switch (upng->color_depth) {
case 8:
return LWS_UPNG_RGBA8;
case 16:
return LWS_UPNG_RGBA16;
default:
return LWS_UPNG_BADFORMAT;
}
default:
return LWS_UPNG_BADFORMAT;
}
}
static const uint8_t magic[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
static lws_stateful_ret_t
lws_upng_decode(lws_upng_t* u, const uint8_t **_pos, size_t *_size)
{
const uint8_t *pos = _pos ? *_pos : NULL, *end = pos + *_size;
lws_stateful_ret_t r = LWS_SRET_FATAL + 60;
size_t m;
if (u->of == UOF_INSIDE && !u->inf.in) {
u->inf.inpos = 0;
u->inf.in = pos;
u->inf.bp = 0;
m = lws_ptr_diff_size_t(end, pos);
if (m > u->chunklen)
m = u->chunklen;
u->inf.inlen = m;
}
while (!u->no_more_input &&
((u->of == UOF_INSIDE && _pos == NULL) || pos < end)) {
switch (u->of) {
case UOF_MAGIC:
if (*pos++ != magic[u->sctr++])
return LWS_SRET_FATAL + 17;
if (u->sctr == sizeof(magic)) {
u->of++;
u->sctr = 0;
}
break;
case UOF_SKIP:
pos++;
if (++u->sctr == 4) {
u->of++;
u->sctr = 0;
}
break;
case UOF_TYPE4:
u->acc = (u->acc << 8) | *pos++;
if (++u->sctr == 4) {
if (u->acc != LWS_FOURCC('I','H','D','R'))
return LWS_SRET_FATAL + 18;
u->of++;
u->sctr = 0;
}
break;
case UOF_WIDTH4:
u->acc = (u->acc << 8) | *pos++;
if (++u->sctr == 4) {
u->width = u->acc;
if (!u->acc)
return LWS_SRET_FATAL + 18;
u->of++;
u->sctr = 0;
}
break;
case UOF_HEIGHT4:
u->acc = (u->acc << 8) | *pos++;
if (++u->sctr == 4) {
u->height = u->acc;
u->of++;
u->sctr = 0;
}
break;
case UOF_CDEPTH:
u->color_depth =*pos++;
u->of++;
break;
case UOF_CTYPE:
u->color_type = *pos++;
//lwsl_notice("w %d, h %d, depth %d, type %d\n",
// u->width, u->height,
// u->color_depth, u->color_type);
u->format = determine_format(u);
if (u->format == LWS_UPNG_BADFORMAT)
return LWS_SRET_FATAL + 19;
u->of++;
break;
case UOF_ONLY_ZERO3:
if (*pos++)
return LWS_SRET_FATAL + 20;
if (++u->sctr == 3) {
u->of++;
u->sctr = 0;
}
break;
case UOF_SKIP4:
pos++;
if (++u->sctr != 4)
break;
/* takes us to +33 */
memset(&u->inf, 0, sizeof(u->inf));
/* 32KB gz sliding window */
u->inf.info_size = 32768 + 512;
u->u.bpp = lws_upng_get_bpp(u);
if (!u->u.bpp)
return LWS_SRET_FATAL + 14;
u->u.y = 0;
u->u.ibp = 0;
u->u.bypp = (u->u.bpp + 7) / 8;
u->inf.bypl = u->u.bypl = u->width * u->u.bypp;
u->inf.outlen = u->inf.info_size;
u->inf.outpos = 0;
u->inf.state = UPNS_ID_BL_GB_DONE;
u->inf.upng = u;
u->u.alt = 0; /* which of the two lines to write to */
u->u.padded = u->u.bpp < 8 &&
u->width * u->u.bpp !=
((u->width * u->u.bpp + 7) / 8) * 8;
u->u.diff = (((u->width * u->u.bpp + 7) / 8) * 8) -
(u->width * u->u.bpp);
u->of++;
u->sctr = 0;
break;
case UOF_CHUNK_LEN:
if (!u->inf.out) {
size_t ims = (u->u.bypl * 2) + u->inf.info_size;
if (u->u.bypl > UINT_MAX / 2 || u->inf.info_size > UINT_MAX - (u->u.bypl * 2)) {
lwsl_err("%s: integer overflow occur in ims %llu",
__func__, (unsigned long long)ims);
return LWS_SRET_FATAL + 27;
}
if (u->hold_at_metadata)
return LWS_SRET_AWAIT_RETRY;
u->inf.out = (uint8_t *)lws_malloc(ims, __func__);
if (!u->inf.out) {
lwsl_notice("%s: inf malloc %u OOM\n",
__func__, (unsigned int)ims);
return LWS_SRET_YIELD;
}
u->u.lines = u->inf.out + u->inf.info_size;
u->u.in = u->inf.out;
}
u->chunklen = (u->chunklen << 8) | *pos++;
if (++u->sctr == 4) {
u->of++;
u->sctr = 0;
}
break;
case UOF_CHUNK_TYPE:
u->ctype = (u->ctype << 8) | *pos++;
if (++u->sctr != 4)
break;
u->sctr = 0;
if (u->ctype == LWS_FOURCC('I','E','N','D')) {
u->no_more_input = 1;
break;
}
if (u->ctype != LWS_FOURCC('I','D','A','T')) {
if (!(u->ctype & (32 << 24)))
/* says it is critical... */
return LWS_SRET_FATAL + 27;
u->chunklen += 4; /* chunk-end CRC */
/* noncritical, skip */
u->of = UOF_SKIP_CHUNK_LEN;
break;
}
if (u->chunklen < 2)
return LWS_SRET_FATAL + 31;
/* it's a usable IDAT */
if (!u->inf.subsequent)
u->inf.inpos = 2;
else
u->inf.inpos = 0;
m = lws_ptr_diff_size_t(end, pos);
if (m > u->chunklen)
m = u->chunklen;
u->inf.in = pos;
u->inf.inlen = m;
u->inf.bp = 0;
u->of++;
break;
case UOF_INSIDE:
if (!u->inf.subsequent) {
switch (u->sctr) {
case 0:
u->acc = (uint32_t)((*pos++) << 8);
u->sctr++;
continue;
case 1:
u->acc |= *pos++;
if (u->acc % 31)
return LWS_SRET_FATAL + 31;
if (((u->acc >> 8) & 15) != 8 ||
((u->acc >> 12) & 15) > 7)
return LWS_SRET_FATAL + 31;
if ((u->acc >> 5) & 1)
return LWS_SRET_FATAL + 31;
u->inf.subsequent = 1;
break;
}
}
r = _lws_upng_inflate_data(&u->inf);
switch (r) {
case LWS_SRET_WANT_INPUT:
/* indicate no existing to drain */
u->inf.in = NULL;
pos += u->inf.inlen - u->inf.inpos;
u->chunklen = u->chunklen -
(unsigned int)(u->inf.inlen);
if (!u->chunklen) {
u->chunklen = 4; /* skip the 32-bit CRC */
u->of = UOF_SKIP_CHUNK_LEN;
break;
}
if (pos != end) {
u->inf.inpos = 0;
u->inf.in = pos;
m = lws_ptr_diff_size_t(end, pos);
if (m > u->chunklen)
m = u->chunklen;
u->inf.inlen = m;
continue;
}
goto bail;
default:
goto bail;
}
break;
case UOF_SKIP_CHUNK_LEN:
pos++;
if (!--u->chunklen) {
u->of = UOF_CHUNK_LEN;
u->sctr = 0;
break;
}
break;
}
}
r = LWS_SRET_OK;
if (!u->no_more_input)
r = LWS_SRET_WANT_INPUT;
bail:
*_pos = pos;
*_size = lws_ptr_diff_size_t(end, pos);
return r;
}
lws_upng_t *
lws_upng_new(void)
{
lws_upng_t* upng;
upng = (lws_upng_t*)lws_zalloc(sizeof(lws_upng_t), __func__);
if (upng == NULL)
return NULL;
upng->color_type = LWS_UPNG_RGBA;
upng->color_depth = 8;
upng->format = LWS_UPNG_RGBA8;
upng->of = UOF_MAGIC;
upng->sctr = 0;
upng->inf.upng = upng;
return upng;
}
void
lws_upng_free(lws_upng_t** upng)
{
if ((*upng)->inf.out)
lws_free_set_NULL((*upng)->inf.out);
lws_free_set_NULL(*upng);
}
unsigned int
lws_upng_get_width(const lws_upng_t* upng)
{
return upng->width;
}
unsigned int
lws_upng_get_height(const lws_upng_t* upng)
{
return upng->height;
}
unsigned int
lws_upng_get_bpp(const lws_upng_t* upng)
{
return lws_upng_get_bitdepth(upng) *
lws_upng_get_components(upng);
}
unsigned int
lws_upng_get_components(const lws_upng_t* upng)
{
switch (upng->color_type) {
case LWS_UPNG_LUM:
return 1;
case LWS_UPNG_RGB:
return 3;
case LWS_UPNG_LUMA:
return 2;
case LWS_UPNG_RGBA:
return 4;
default:
return 0;
}
}
unsigned int
lws_upng_get_bitdepth(const lws_upng_t* upng)
{
return upng->color_depth;
}
unsigned int
lws_upng_get_pixelsize(const lws_upng_t* upng)
{
unsigned bits = lws_upng_get_bitdepth(upng) *
lws_upng_get_components(upng);
bits += bits % 8;
return bits;
}
lws_upng_format_t
lws_upng_get_format(const lws_upng_t *upng)
{
return upng->format;
}