diff --git a/CMakeLists.txt b/CMakeLists.txt index db4e8c246ea3bbaadd4265bdb9164407120688aa..6367ddf4bcef906299c21eec02d56168e036cf51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,8 @@ option(LWS_WITHOUT_DAEMONIZE "Don't build the daemonization api" ON) option(LWS_SSL_SERVER_WITH_ECDH_CERT "Include SSL server use ECDH certificate" OFF) option(LWS_WITH_LEJP "With the Lightweight JSON Parser" ON) option(LWS_WITH_SQLITE3 "Require SQLITE3 support" OFF) +option(LWS_WITH_STRUCT_JSON "Generic struct serialization to and from JSON" ON) +option(LWS_WITH_STRUCT_SQLITE3 "Generic struct serialization to and from SQLITE3" OFF) option(LWS_WITH_SMTP "Provide SMTP support" OFF) if (WIN32 OR LWS_WITH_ESP32) option(LWS_WITH_DIR "Directory scanning api support" OFF) @@ -189,6 +191,10 @@ if (NOT LWS_WITH_NETWORK) set(LWS_WITH_POLL 0) endif() +if (LWS_WITH_STRUCT_SQLITE3) + set(LWS_WITH_SQLITE3 1) +endif() + # do you care about this? Then send me a patch where it disables it on travis # but allows it on APPLE if (APPLE) @@ -898,7 +904,10 @@ set(HDR_PUBLIC set(SOURCES lib/core/alloc.c + lib/core/buflist.c lib/core/context.c + lib/core/lws_dll.c + lib/core/lws_dll2.c lib/core/libwebsockets.c lib/core/logs.c lib/misc/base64-decode.c @@ -1020,6 +1029,16 @@ if (LWS_WITH_DISKCACHE) lib/misc/diskcache.c) endif() +if (LWS_WITH_STRUCT_JSON) + list(APPEND SOURCES + lib/misc/lws-struct-lejp.c) +endif() + +if (LWS_WITH_STRUCT_SQLITE3) + list(APPEND SOURCES + lib/misc/lws-struct-sqlite.c) +endif() + if (NOT LWS_WITHOUT_CLIENT) list(APPEND SOURCES lib/core-net/connect.c diff --git a/READMEs/README.lws_struct.md b/READMEs/README.lws_struct.md new file mode 100644 index 0000000000000000000000000000000000000000..00ca08abd2d7faf913ba728ce12c8040b61aa6f4 --- /dev/null +++ b/READMEs/README.lws_struct.md @@ -0,0 +1,38 @@ +# lws_struct + +## Overview + +lws_struct provides a lightweight method for serializing and deserializing C +structs to and from JSON, and to and from sqlite3. + + + + - you provide a metadata array describing struct members one-time, then call + generic apis to serialize and deserialize + + - supports flat structs, single child struct pointers, and unbounded arrays / + linked-lists of child objects automatically using [lws_dll2 linked-lists](./README.lws_dll.md) + + - supports boolean and C types char, int, long, long long in explicitly signed + and unsigned forms + + - supports both char * type string members where the unbounded content is + separate and pointed to, and fixed length char array[] type members where + the content is part of the struct + + - huge linear strings are supported by storing to a temp lwsac of chained chunks, + which is written into a single linear chunk in the main lwsac once the + total string length is known + + - deserialization allocates into an [lwsac](../lib/misc/lwsac/README.md), so everything is inside as few + heap allocations as possible while still able to expand to handle arbitrary + array or strins sizes + + - when deserialized structs are finished with, a single call to free the + lwsac frees the whole thing without having to walk it + + - stateful serializaton and deserialization allows as-you-get packets incremental + parsing and production of chunks of as-you-can-send incremental serialization + output cleanly + +## Examples diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index 70cfa59bc16a5af236590f5a442718d653c71477..5b1be9b61653b7b41c3c53363eb0a201bbfaa698 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -116,6 +116,8 @@ #cmakedefine LWS_WITH_SOCKS5 #cmakedefine LWS_WITH_STATEFUL_URLDECODE #cmakedefine LWS_WITH_STATS +#cmakedefine LWS_WITH_STRUCT_SQLITE3 +#cmakedefine LWS_WITH_SQLITE3 #cmakedefine LWS_WITH_THREADPOOL #cmakedefine LWS_WITH_TLS #cmakedefine LWS_WITH_UNIX_SOCK diff --git a/doc-assets/lws_struct-overview.svg b/doc-assets/lws_struct-overview.svg new file mode 100644 index 0000000000000000000000000000000000000000..ba618c9a03e15be7f67f6a1d2ac713d22a010a10 --- /dev/null +++ b/doc-assets/lws_struct-overview.svg @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="258.28mm" height="107.81mm" version="1.1" viewBox="0 0 258.28 107.81" xmlns="http://www.w3.org/2000/svg"> + <defs> + <marker id="a" overflow="visible" orient="auto"> + <path transform="matrix(.2 0 0 .2 1.2 0)" d="m0 0 5-5-17.5 5 17.5 5z" fill-rule="evenodd" stroke="#000" stroke-width="1pt"/> + </marker> + <marker id="e" overflow="visible" orient="auto"> + <path transform="matrix(-.3 0 0 -.3 .69 0)" d="m8.7186 4.0337-10.926-4.0177 10.926-4.0177c-1.7455 2.3721-1.7354 5.6175-6e-7 8.0354z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/> + </marker> + <marker id="b" overflow="visible" orient="auto"> + <path transform="matrix(.2 0 0 .2 1.2 0)" d="m0 0 5-5-17.5 5 17.5 5z" fill-rule="evenodd" stroke="#000" stroke-width="1pt"/> + </marker> + <marker id="f" overflow="visible" orient="auto"> + <path transform="matrix(-.3 0 0 -.3 .69 0)" d="m8.7186 4.0337-10.926-4.0177 10.926-4.0177c-1.7455 2.3721-1.7354 5.6175-6e-7 8.0354z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/> + </marker> + <marker id="c" overflow="visible" orient="auto"> + <path transform="matrix(.2 0 0 .2 1.2 0)" d="m0 0 5-5-17.5 5 17.5 5z" fill-rule="evenodd" stroke="#000" stroke-width="1pt"/> + </marker> + <marker id="g" overflow="visible" orient="auto"> + <path transform="matrix(-.3 0 0 -.3 .69 0)" d="m8.7186 4.0337-10.926-4.0177 10.926-4.0177c-1.7455 2.3721-1.7354 5.6175-6e-7 8.0354z" fill-rule="evenodd" stroke="#000" stroke-linejoin="round" stroke-width=".625"/> + </marker> + <filter id="d" x="-.03148" y="-.082687" width="1.063" height="1.1654" color-interpolation-filters="sRGB"> + <feGaussianBlur stdDeviation="1.2468962"/> + </filter> + </defs> + <g transform="translate(751.29 58.975)"> + <g> + <rect transform="matrix(2.5561 0 0 2.5561 1169.1 91.771)" x="-748.3" y="-55.983" width="95.061" height="36.191" filter="url(#d)"/> + <rect x="-744.61" y="-52.171" width="242.99" height="92.508" fill="#fff"/> + <circle cx="-715.52" cy="-2.6399" r="13.375" fill="#d4aa00"/> + <text x="-715.49457" y="-0.013140791" dominant-baseline="auto" fill="#000000" font-family="'Open Sans Condensed'" font-size="9.0174px" letter-spacing="0px" stroke-width=".6763" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-715.49457" y="-0.013140791" stroke-width=".6763">sqlite</tspan></text> + <circle cx="-671.6" cy="-2.9116" r="13.375" fill="#b3b3b3"/> + <text x="-671.56494" y="-0.28489438" dominant-baseline="auto" fill="#000000" font-family="'Open Sans Condensed'" font-size="9.0174px" letter-spacing="0px" stroke-width=".6763" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-671.56494" y="-0.28489438" stroke-width=".6763">C structs</tspan></text> + <circle cx="-627.82" cy="-2.67" r="13.375" fill="#aad400"/> + </g> + <g fill="#000000" font-family="'Open Sans Condensed'" font-size="9.0174px" letter-spacing="0px" stroke-width=".6763" text-anchor="middle" word-spacing="0px"> + <text x="-627.78656" y="-0.043329135" dominant-baseline="auto" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-627.78656" y="-0.043329135" stroke-width=".6763">JSON</tspan></text> + <text x="-627.64801" y="21.166273" dominant-baseline="auto" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-627.64801" y="21.166273" stroke-width=".6763">transit</tspan></text> + <text x="-716.26086" y="21.211004" dominant-baseline="auto" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-716.26086" y="21.211004" stroke-width=".6763">storage</tspan></text> + <text x="-672.80682" y="20.817616" dominant-baseline="auto" text-align="center" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-672.80682" y="20.817616" stroke-width=".6763">processing</tspan></text> + </g> + <g fill="none" stroke="#000" stroke-width="1.7147"> + <path d="m-699.88-2.5175 13.015-0.1388" marker-end="url(#g)" marker-start="url(#c)" stroke-dasharray="3.42934834, 1.71467418"/> + <path d="m-655.78-2.6715 13.015-0.1388" marker-end="url(#f)" marker-start="url(#b)"/> + <path d="m-611.7-3.5168 13.015-0.1388" marker-end="url(#e)" marker-start="url(#a)"/> + </g> + <g transform="matrix(.98084 0 0 .98084 -621.3 237.27)" fill="#000080" opacity=".244"> + <path d="m41.881-252.66c-0.74224 2.5432-0.89347 6.2107 2.3684 9.2701"/> + <path d="m53.529-227.18c6.6092-4.0094 3.4326-11.99-0.99884-13.439-1.8556-0.60663-5.8343 0.0294-7.9178 3.1773-0.58269 0.88034-2.1307 3.9439-0.48031 5.5996 2.9809 2.6693 5.6161 0.56537 5.3608-1.4101-0.21066-1.6304-2.5195-2.2246-3.5161-0.16091-0.25975-2.8413 3.6465-3.1988 4.5934-2.4826 3.041 2.3001 1.5823 6.4615 1.0276 7.1414-0.73173 0.89688-3.6971 3.6214-8.2393 0.56437-6.8298-5.655-0.13193-11.313 3.866-12.476 6.1028-2.0341 9.0102 1.5223 10.671 3.6144 3.6868 4.6452 1.9809 8.9627 5.3505 13.525 1.9595 3.128 7.9821 3.7261 11.158 0.60173 5.5288-6.1445 3.3332-13.229-1.2516-13.479-0.22801-0.0125-2.9101-0.63086-4.4241 2.3627-1.0649 2.794 4e-3 3.8072 0.81726 4.1078 2.9088 0.79976 4.5016-5.174 0.82228-3.1128 4.8236-4.2183 5.2182 0.96693 5.0018 2.2308-0.80165 5.564-9.3442 7.4818-10.819 2.8668-0.35205-0.62503-1.4338-5.0762 3.9067-8.1954 5.5765-3.6326 13.211 5.2134 17.982 5.8918 10.106 1.6835 14.307-6.6479 10.856-12.582-1.2554-1.7669-2.8684-3.5441-5.7068-3.7758-6.1109-0.17391-9.2141 3.4739-7.9593 8.2436 1.3959 2.9346 4.6564 2.9335 5.6551 2.6689 10.271-4.9733-4.2693-8.9086-0.87901-1.2245-4.8802-3.8972 0.63964-7.5993 4.4048-6.1991 0.96063 0.35724 5.1803 5.0648-0.05897 9.3556-1.1663 0.95517-4.5269 2.2051-8.0004 0.28109-3.3694-1.8664-3.5338-5.8701-2.477-9.261 1.0569-3.3909 5.3014-5.0232 8.3812-4.6644 6.7277 0.78392 8.7315 5.091 9.5955 7.5454 2.4366 6.9213-4.6408 13.804-8.902 13.4-4.2109 0.0282-8.4677 0.21333-12.52-6.2854 2.1202 3.9189 2.0498 12.14-3.7631 14.191-4.5313 1.8257-11.466 2.5989-16.992-3.7837-8.6613 4.8198-20.705 1.566-24.156-7.0006-0.92495-2.578-0.47339-6.8624 0.24474-9.4071-7.6693 1.5897-10.613-4.3062-9.6483-8.4011 0.91606-4.9175 6.1951-6.9841 9.8568-7.0428 1.5425-0.0247 4.6646-0.23374 6.5237 1.4445 0.91307 0.82426 0.56096-3.1566 4.8371-7.1831 1.0571-1.2067 4.9098-4.4309 6.8716-4.0876 2.9444 0.51523 6.0699-5.719 15.148-4.942 3.6186 0.30968 11.982 4.7173 15.123 5.2824 6.4081 1.1531 11.088-2.2 14.812 1.3207 2.1216 2.0055 4.2287 8.5906 1.9089 12.056-3.3431 5.2297 0.78824 7.9425 0.71094 11.137-0.41729 1.0853-2.8169-2.0166-3.8145-6.0592-0.12602-0.51067-1.1007-2.262-2.1876-2.0061 5.211-1.0111 1.7392-4.8923 3.8526-5.3799-1.3772 2.9802-4.6943 6.3834-11.832 6.001 2.3202-2.0058 7.0672-2.1803 9.3859-7.409 1.4893-3.3583-1.1162-6.6918-1.6409-7.4946-1.7761-2.7172-9.3712-0.53945-10.376 0.18939-1.5682 1.1375-2.5616 5.9494-0.43858 6.8512 0.54995 0.23361 1.794 0.56034 3.5295 0.0399 0.50862-0.15251 2.2807-1.4805 1.5209-3.1415-0.75989-1.661-4.1817-1.9196-3.2935 2.0787-2.4306-2.4398 0.42917-5.7946 3.2902-5.3916 0.79279 0.11167 2.0901 0.64339 2.7036 1.9892 0.7832 1.7182 0.04247 9.0743-6.3208 9.7095-2.5704-0.23817-4.933-1.0036-6.0081-4.2173-1.0894-3.2565-0.34649-6.6133 3.6075-8.8417-10.088-5.6172-13.564-5.3423-14.931-5.3234-3.5985 0.0499-11.415 2.2097-12.233 6.9656-2.0539 9.4145 6.0196 15.718 11.313 13.869 2.0435-0.71404 5.3178-3.0132 6.2069-5.7695 1.1253-2.7161 0.52067-9.2257-3.7502-9.8187-7.5674-1.0507-7.4178 5.1207-7.3467 5.7392 0.43169 3.7568 3.3857 3.0344 3.6417 2.0547 0.95883-3.6692-2.0573-0.28653-1.6915-1.0436 0.46602-0.96437 3.7458-4.2325 5.2541-0.60127 1.7856 4.2987-2.9558 7.0032-5.6537 6.3922-4.4526-1.0083-5.5626-5.3976-4.766-8.0601 2.1592-7.2172 11.062-5.6806 12.605-5.1024 5.4016 2.0239 6.2208 7.6173 5.5996 10.861-0.49155 2.5665-2.0196 9.0474-10.012 9.7885-4.1054 0.38065-9.5771-2.4666-11.216-5.3882-2.0253-3.6104-3.5546-6.4056-1.5183-12.344 0.49778-1.4517-0.80522-2.6114-2.1376-2.7358-1.8908-0.17651-7.8322 5.0017-8.3951 12.347-0.0049 0.0687 0.16434 3.8731 1.508 5.7369 1.0084 1.3987 1.5571 1.0966 3.0688 0.64569-0.50956 1.6452-2.9656 3.1932-5.2687 0.96907-1.1202-1.0819-1.4076-4.8291-2.1762-7.3776-0.52918-1.7545-1.3818-2.0825-1.8102-2.3238-10.206-1.1153-13.561 6.2269-9.7615 11.16 1.0884 1.2644 4.3886 2.667 7.0879-0.23624 0.9211-1.4945 1.569-5.634-2.188-6.2928-2.8126-0.35877-4.3231 2.5961-2.3307 4.1754 0.37764 0.29935 2.4183 0.24252 2.3959-1.3755 2.2889 2.8495-1.3845 4.3098-3.6108 2.018-1.7839-2.9588 1.0791-5.2149 3.1674-5.1311-0.18791-0.022 5.895-0.84785 4.9726 6.1419-4.9602 11.939 4.7226 21.559 15.224 15.13z"/> + <path d="m49.624-251.08c4.0745-1.5884-4.5287-6.9179-2.0933 1.9722 3.3605 7.6759 16.386 13.929 25.972 10.856 6.2802-2.3279 6.422-8.3769 11.798-9.3185-1.4058 0.498-3.0762-0.99605-8.0373 3.492 2.7673-2.8522 0.46911-8.4801 0.5153-10.38 0.81305 6.522-3.3474 14.573-10.648 15.099-9.1475-0.20557-17.567-6.4219-18.317-10.157-1.3969-3.3934 1.9255-4.3416 0.80968-1.5631z"/> + <path d="m65.991-266.93c-3.2975 0.0765-7.0461 1.7511-9.1722 4.3069-1.4452 1.8513-1.5312 4.3552-1.5178 6.6306 0.40978 2.9471 0.52755-2.4046 1.0549-3.2704 0.79541-3.0328-0.05729 1.3697 0.19279 2.5077-3e-3 1.4284 0.62634 4.1837 0.98556 4.6393-0.36808-2.9118-0.83127-5.9611-0.0089-8.831 1.1023-1.717-1.0043 5.3378-0.05502 2.4936 0.4507-2.5768 3.4798-4.9071 5.958-5.5887 1.9113-0.44792 3.8363 0.0423 5.7521 0.13584-1.5893-1.1774-4.5231-0.98014-5.3358-0.94935 1.9261-0.94969 4.1318-0.0705 6.1133 0.25724 0.92665 0.28009 6.1313 2.5542 3.7244 1.4918-2.7444-2.1779-4.7658-2.532-8.0506-2.962 2.403-0.17544 4.792 0.0365 7.8314 1.5548 1.0265 0.36413 3.0918 2.8212 3.2862 2.578-1.2943-1.7886-1.656-2.829-5.0375-4.2074-2.3484-0.68646-3.2712-0.65201-5.7213-0.78692z"/> + <path d="m58.364-238.03c0.82342 0.96459 1.6468 1.9292 2.4703 2.8938 0.7972 4.0979 0.99109 8.6917 3.935 11.943 1.529 1.3148 3.8631 1.8335 5.5898 0.94221-1.6708 0.0763-3.301-0.33307-4.7173-1.19-1.0351-0.45373-2.6418-3.7051-0.93718-1.5735 1.7152 1.2122 3.8589 2.519 6.0318 1.776 1.6617-0.48301 3.0361-1.5936 3.9635-3.0436 0.658-0.86497-1.5494 1.0574-2.0538 1.3326-1.8192 1.5865-4.345 0.99565-6.3502 0.16388-1.5572-1.0506-3.0636-2.5037-3.4345-4.4401-0.53767-2.1605 0.98445-4.2317 2.4081-5.6982 1.6672-1.9048-2.3171 0.34388-2.1141 1.6664-1.0943 2.7953-0.80067-1.9094 0.29333-2.6286 1.0637-1.4519-1.4052-0.44253-1.242 0.76276-0.18207 0.78531-0.7779 2.569-0.75391 0.63336 0.26016-2.0878-1.6642-2.6196-3.0889-3.5399z"/> + <path d="m28.156-247.06c0.92848-3.4966 5.0287-4.4196 8.1746-4.5432 2.7482-0.098 3.9609 2.5301 3.7359 4.9102 1.2078 1.8932 1.8939 5.2494 4.7221 5.2123 1.2565-0.038 4.8122-0.79982 1.7535 0.0858-1.4333 0.12222-3.256 0.27432-3.2963 0.71318-2.8124-0.78131-1.9305 3.4262-3.2886 4.988-0.84011 2.5666 0.06825 5.4727 1.5198 7.6219-2.1035-1.442-2.5373-4.3132-2.6564-6.6511-1.0429 0.61507 0.02588 3.9157-0.70499 1.3105-0.96281-3.0077 0.12061-6.1571 1.2057-8.9638-0.28831 1.356-1.0953 5.2215-0.54995 4.9051 0.17316-2.8227 2.8539-6.6846 0.33375-8.8214-0.47624-1.622-3.5311-2.5422-3.7271-2.9056 1.0824-0.19259 4.4689 1.1444 2.3851-0.89256-1.7961-1.5634-4.5102-0.71819-6.2136 0.55886-0.92245 1.0856-0.90091 1.8597-1.0907 0.0721-0.8937 0.11462-1.6812 1.692-2.3029 2.3998z"/> + <path d="m45.236-235.2c1.4188-1.2781 3.3971-2.7545 5.3421-1.602 2.2217 0.66519 3.4478 3.1534 2.884 5.3536-0.35111 2.2009-1.6599 4.5037-3.921 5.1553 2.5987-0.48934 4.4214-2.8736 4.7078-5.4242 0.1482-0.77718 0.32924-3.0423 0.40115-1.0554 0.12287 1.4282 0.10405 3.0167-0.82412 4.1993 1.4662-1.0739 2.0707-2.9731 1.5762-4.6908-0.45478-0.93908-0.35002-2.0493 0.21589-0.60058 0.29431 0.77353 0.85771 2.5136 0.68046 0.59142 0.10183-2.1526-1.1589-3.992-2.5783-5.468-1.6545-1.5072-4.1397-1.0272-6.0679-0.40689-0.86995 0.3702-2.6266 1.5015-2.405 2.1681 1.5976-1.3604 3.7768-2.0397 5.8513-2.0223 1.1343 0.0526 1.9047 0.93003 0.30213 0.39937-1.4706-0.39468-4.1193 0.64258-4.5509 1.1895 1.2514-0.73692 2.9324-1.0773 4.2523-0.52456-2.0155 0.32148-4.9769-2e-3 -5.7173 2.462z"/> + <path d="m86.737-261.88c2.7563-0.85852 6.3293-0.86997 7.8693 2.0772 2.368 3.7758-0.10823 8.4824-3.8369 10.086-1.0758 0.54064-4.9583 2.5338-2.4181 1.0435 1.7273-0.88976 5.7959-3.6731 5.9537-6.6596-1.5209 2.8566-5.3829 6.2444-5.4944 5.5818 2.2515-1.9622 5.7254-6.7355 4.4017-8.798-0.18086 2.3574-0.74912 3.2298-1.9174 4.6555 1.3091-2.3149 1.4959-5.674-1.6939-7.182-1.3411-0.35708-2.2776-0.17587-3.6745-0.07 3.191-1.0558 7.3728 0.49056 7.9763 1.8864-0.45062-2.8257-4.689-2.7438-7.1658-2.621z"/> + <path d="m75.47-237.09c2.803 1.1663 5.3876 2.75 7.7894 4.5827 1.9392 1.5512 4.4316 2.1984 6.8793 1.8021 2.3819-0.15037 4.6177-1.2797 6.0384-3.2205 1.9103-1.8401 1.691-4.7936 1.1326-7.1714-0.72217-1.5912-1.5238-3.4197-3.1792-4.1842-2.4435-1.655-5.9834-0.92278-8.0631 1.0117-1.5923 0.92202-2.2122 4.5642-1.4908 5.0768-0.0885-1.1957 0.49061-4.0393 1.2515-3.838-0.37329 1.783-0.92585 4.5926 0.50945 5.5734-0.8281-2.4749-0.42119-5.6188 1.6709-7.3533 1.916-0.69165 1.3727 0.0971 0.05383 0.82066-1.0229 0.84309-0.97413 1.8974 0.2291 0.57914 1.6494-1.1529 5.0885-1.3778 6.0631 0.19933-0.90007-0.28761-2.911-1.116-1.022-0.0561 1.9922 0.71869 2.4199 2.8623 2.6766 4.5463 1.1651-3.0198-1.5306-6.5592-4.7208-6.464 2.0653-0.70343 4.5676 1.3093 5.1924 3.4456 0.99083 2.0816 0.44235 4.4699-0.78661 6.3162-0.28779 1.164-3.2359 3.5234-0.83706 1.7868 1.0288-0.62561 1.9327-3.8066 2.276-3.4672-0.62049 2.3505-2.1836 4.9718-4.8418 5.2318-1.685-1.3884-3.9146 1.1436-5.6327 0.0106 1.0791 1.0606 3.711-0.21206 4.0259 0.49175-2.1714 0.97677-4.6633 0.0758-6.5161-1.1913-2.1407-0.61396-3.3669-2.5051-3.7891-4.5991-0.80372-1.169 0.05018-4.2871 0.01116-4.1904-1.3934 1.8704-0.77804 4.5464 0.15394 6.3709-1.4526-1.2408-3.2863-1.6076-5.0744-2.1103z"/> + <path d="m50.816-260.85c-4.7428 2.3195-7.1531 8.0201-6.1682 13.101 0.39021 3.199 4.0638 3.4365 5.7719 5.5891 1.5389 0.65182 2.9375 0.63553 0.71682-0.17495-1.5715-1.7161-4.12-3.0931-5.145-4.8013 1.783-0.2772 4.1569 3.8232 4.4273 3.0339-1.8386-2.4567-5.1454-4.5752-4.3129-8.1415-0.08179-1.1106 1.0497 5.0564 0.75955 1.2857-1.2383-2.9507 0.76703-6.6915 3.6364-3.2944 2.2856 2.3184-1.7431 5.7349 1.4032 7.6768 2.9709 2.6687 6.3811 5.045 10.377 5.8104-2.8244-1.2674-8.8144-4.4477-8.8341-5.6956 3.7202 2.8569 8.0143 5.1295 12.555 6.2946 1.903 0.41675 5.1739-0.28816 5.8167-1.6201-1.0633 0.93888-7.2765 1.5085-3.2054 0.5499 2.3524 0.0776 6.2515-3.239 5.786-3.926-3.7911 3.7293-9.9662 4.6807-14.649 2.0879-2.5619-1.4337-5.342-2.8518-7.136-5.2375-1.3113-2.369 3.214 2.47 0.93224-0.61762-1.1318-2.9407-2.6594-5.4934-4.4296-8.0667 0.12899-2.4985 1.7215-1.6882 1.3476 0.44799 1.3267 0.61265 1.9866 5.1062 2.2629 4.4024 3e-3 -2.2907-2.4535-5.7724-1.0966-7.2729 0.70538 5.5047 2.2634-4.336-0.81591-1.4309z"/> + <path d="m64.434-258.85c0.50641-2.0361 2.6105-3.0205 4.9975-2.1045 2.2245 1.1612 3.1451 4.1428 2.485 6.4831-0.36288 1.6369-0.91876 2.8077-1.8397 4.0217 0.91276-2.6109 2.182-5.7102 0.3283-7.932 0.69965 1.4755 0.09798 4.4636-0.42959 6.0267-0.42961 1.2118-1.9816 2.8979-2.5725 3.3358 1.3169-1.5756 2.7659-3.31 2.7069-5.3954 0.55889-1.9106-0.70491-5.1142-3.0272-4.5658-1.0129 0.0956-3.4269 2.0908-3.4554 1.7915 1.0397-1.6217 3.2371-2.8233 5.0618-2.6442-0.69423-1.545-3.1889-0.0781-4.0742 0.84006z"/> + </g> + <text x="-696.14355" y="-27.217051" dominant-baseline="auto" fill="#000000" font-family="'Open Sans Condensed'" font-size="20.244px" letter-spacing="0px" stroke-width="1.5183" text-align="center" text-anchor="middle" word-spacing="0px" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;line-height:1.25;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal" xml:space="preserve"><tspan x="-696.14355" y="-27.217051" stroke-width="1.5183">lws_struct</tspan></text> + </g> +</svg> diff --git a/include/libwebsockets.h b/include/libwebsockets.h index 2f60a9e8eb70cb7e3d9f6219e98de9a13123a108..31948abae14eb26e87b4381e1b57df4021d0383c 100644 --- a/include/libwebsockets.h +++ b/include/libwebsockets.h @@ -522,6 +522,7 @@ struct lws; #include <libwebsockets/lws-vfs.h> #include <libwebsockets/lws-lejp.h> #include <libwebsockets/lws-stats.h> +#include <libwebsockets/lws-struct.h> #include <libwebsockets/lws-threadpool.h> #include <libwebsockets/lws-tokenize.h> #include <libwebsockets/lws-lwsac.h> diff --git a/include/libwebsockets/lws-lejp.h b/include/libwebsockets/lws-lejp.h index 8e502377d8fbb3a71f0365cf424165bb1a0c6ba8..81a93831310e2c68753c8c6d72ff2e6c5b2d9e56 100644 --- a/include/libwebsockets/lws-lejp.h +++ b/include/libwebsockets/lws-lejp.h @@ -173,6 +173,9 @@ LWS_EXTERN signed char _lejp_callback(struct lejp_ctx *ctx, char reason); typedef signed char (*lejp_callback)(struct lejp_ctx *ctx, char reason); +#ifndef LEJP_MAX_PARSING_STACK_DEPTH +#define LEJP_MAX_PARSING_STACK_DEPTH 5 +#endif #ifndef LEJP_MAX_DEPTH #define LEJP_MAX_DEPTH 12 #endif @@ -201,25 +204,36 @@ struct _lejp_stack { char b; /* user bitfield */ }; +struct _lejp_parsing_stack { + void *user; /* private to the stack level */ + signed char (*callback)(struct lejp_ctx *ctx, char reason); + const char * const *paths; + uint8_t count_paths; + uint8_t ppos; + uint8_t path_match; +}; + struct lejp_ctx { /* sorted by type for most compact alignment * * pointers */ - - signed char (*callback)(struct lejp_ctx *ctx, char reason); void *user; - const char * const *paths; /* arrays */ + struct _lejp_parsing_stack pst[LEJP_MAX_PARSING_STACK_DEPTH]; struct _lejp_stack st[LEJP_MAX_DEPTH]; uint16_t i[LEJP_MAX_INDEX_DEPTH]; /* index array */ uint16_t wild[LEJP_MAX_INDEX_DEPTH]; /* index array */ char path[LEJP_MAX_PATH]; char buf[LEJP_STRING_CHUNK + 1]; + /* size_t */ + + size_t path_stride; /* 0 means default ptr size, else stride */ + /* int */ uint32_t line; @@ -235,11 +249,11 @@ struct lejp_ctx { uint8_t f; uint8_t sp; /* stack head */ uint8_t ipos; /* index stack depth */ - uint8_t ppos; uint8_t count_paths; uint8_t path_match; uint8_t path_match_len; uint8_t wildcount; + uint8_t pst_sp; /* parsing stack head */ }; LWS_VISIBLE LWS_EXTERN void @@ -257,6 +271,21 @@ LWS_VISIBLE LWS_EXTERN void lejp_change_callback(struct lejp_ctx *ctx, signed char (*callback)(struct lejp_ctx *ctx, char reason)); +/* + * push the current paths / paths_count and lejp_cb to a stack in the ctx, and + * start using the new ones + */ +LWS_VISIBLE LWS_EXTERN int +lejp_parser_push(struct lejp_ctx *ctx, void *user, const char * const *paths, + unsigned char paths_count, lejp_callback lejp_cb); + +/* + * pop the previously used paths / paths_count and lejp_cb, and continue + * parsing using those as before + */ +LWS_VISIBLE LWS_EXTERN int +lejp_parser_pop(struct lejp_ctx *ctx); + /* exported for use when reevaluating a path for use with a subcontext */ LWS_VISIBLE LWS_EXTERN void lejp_check_path_match(struct lejp_ctx *ctx); diff --git a/include/libwebsockets/lws-misc.h b/include/libwebsockets/lws-misc.h index 7ed1490dd62740286015d3f2b93b5cff6c8c9f1d..971bfa04228e557711abf8c6f219142bdc667e4e 100644 --- a/include/libwebsockets/lws-misc.h +++ b/include/libwebsockets/lws-misc.h @@ -241,18 +241,19 @@ lws_dll_foreach_safe(struct lws_dll *phead, void *user, struct lws_dll2; struct lws_dll2_owner; -struct lws_dll2 { +typedef struct lws_dll2 { struct lws_dll2 *prev; struct lws_dll2 *next; struct lws_dll2_owner *owner; -}; +} lws_dll2_t; -struct lws_dll2_owner { +typedef struct lws_dll2_owner { struct lws_dll2 *tail; struct lws_dll2 *head; uint32_t count; -}; +} lws_dll2_owner_t; + static LWS_INLINE int lws_dll2_is_detached(const struct lws_dll2 *d) { return !d->owner; } @@ -278,6 +279,12 @@ LWS_VISIBLE LWS_EXTERN int lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user, int (*cb)(struct lws_dll2 *d, void *user)); +LWS_VISIBLE LWS_EXTERN void +lws_dll2_clear(struct lws_dll2 *d); + +LWS_VISIBLE LWS_EXTERN void +lws_dll2_owner_clear(struct lws_dll2_owner *d); + /* * these are safe against the current container object getting deleted, * since the hold his next in a temp and go to that next. ___tmp is @@ -332,6 +339,7 @@ lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf, */ LWS_VISIBLE LWS_EXTERN size_t lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf); + /** * lws_buflist_use_segment(): remove len bytes from the current segment * @@ -349,6 +357,7 @@ lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf); */ LWS_VISIBLE LWS_EXTERN int lws_buflist_use_segment(struct lws_buflist **head, size_t len); + /** * lws_buflist_destroy_all_segments(): free all segments on the list * diff --git a/include/libwebsockets/lws-struct.h b/include/libwebsockets/lws-struct.h new file mode 100644 index 0000000000000000000000000000000000000000..0d02f59e5141b929c07fd0f5ad41d3491d208645 --- /dev/null +++ b/include/libwebsockets/lws-struct.h @@ -0,0 +1,258 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * included from libwebsockets.h + */ + +#if defined(LWS_WITH_STRUCT_SQLITE3) +#include <sqlite3.h> +#endif + +typedef enum { + LSMT_SIGNED, + LSMT_UNSIGNED, + LSMT_BOOLEAN, + LSMT_STRING_CHAR_ARRAY, + LSMT_STRING_PTR, + LSMT_LIST, + LSMT_CHILD_PTR, + LSMT_SCHEMA, + +} lws_struct_map_type_eum; + +typedef struct lejp_collation { + struct lws_dll2 chunks; + int len; + char buf[LEJP_STRING_CHUNK + 1]; +} lejp_collation_t; + +typedef struct lws_struct_map { + const char *colname; + const struct lws_struct_map *child_map; + lejp_callback lejp_cb; + size_t ofs; /* child dll2; points to dll2_owner */ + size_t aux; + size_t ofs_clist; + size_t child_map_size; + lws_struct_map_type_eum type; +} lws_struct_map_t; + +typedef int (*lws_struct_args_cb)(void *obj, void *cb_arg); + +typedef struct lws_struct_args { + const lws_struct_map_t *map_st[LEJP_MAX_PARSING_STACK_DEPTH]; + lws_struct_args_cb cb; + struct lwsac *ac; + void *cb_arg; + void *dest; + + size_t dest_len; + size_t toplevel_dll2_ofs; + size_t map_entries_st[LEJP_MAX_PARSING_STACK_DEPTH]; + size_t ac_block_size; + int subtype; + + /* + * temp ac used to collate unknown possibly huge strings before final + * allocation and copy + */ + struct lwsac *ac_chunks; + struct lws_dll2_owner chunks_owner; + size_t chunks_length; +} lws_struct_args_t; + +#define LSM_SIGNED(type, name, qname) \ + { \ + qname, \ + NULL, \ + NULL, \ + offsetof(type, name), \ + sizeof ((type *)0)->name, \ + 0, \ + 0, \ + LSMT_SIGNED \ + } + +#define LSM_UNSIGNED(type, name, qname) \ + { \ + qname, \ + NULL, \ + NULL, \ + offsetof(type, name), \ + sizeof ((type *)0)->name, \ + 0, \ + 0, \ + LSMT_UNSIGNED \ + } + +#define LSM_BOOLEAN(type, name, qname) \ + { \ + qname, \ + NULL, \ + NULL, \ + offsetof(type, name), \ + sizeof ((type *)0)->name, \ + 0, \ + 0, \ + LSMT_BOOLEAN \ + } + +#define LSM_CARRAY(type, name, qname) \ + { \ + qname, \ + NULL, \ + NULL, \ + offsetof(type, name), \ + sizeof (((type *)0)->name), \ + 0, \ + 0, \ + LSMT_STRING_CHAR_ARRAY \ + } + +#define LSM_STRING_PTR(type, name, qname) \ + { \ + qname, \ + NULL, \ + NULL, \ + offsetof(type, name), \ + sizeof (((type *)0)->name), \ + 0, \ + 0, \ + LSMT_STRING_PTR \ + } + +#define LSM_LIST(ptype, pname, ctype, cname, lejp_cb, cmap, qname) \ + { \ + qname, \ + cmap, \ + lejp_cb, \ + offsetof(ptype, pname), \ + sizeof (ctype), \ + offsetof(ctype, cname), \ + LWS_ARRAY_SIZE(cmap), \ + LSMT_LIST \ + } + +#define LSM_CHILD_PTR(ptype, pname, ctype, lejp_cb, cmap, qname) \ + { \ + qname, \ + cmap, \ + lejp_cb, \ + offsetof(ptype, pname), \ + sizeof (ctype), \ + 0, \ + LWS_ARRAY_SIZE(cmap), \ + LSMT_CHILD_PTR \ + } + +#define LSM_SCHEMA(ctype, lejp_cb, map, schema_name) \ + { \ + schema_name, \ + map, \ + lejp_cb, \ + 0, \ + sizeof (ctype), \ + 0, \ + LWS_ARRAY_SIZE(map), \ + LSMT_SCHEMA \ + } + +#define LSM_SCHEMA_DLL2(ctype, cdll2mem, lejp_cb, map, schema_name) \ + { \ + schema_name, \ + map, \ + lejp_cb, \ + offsetof(ctype, cdll2mem), \ + sizeof (ctype), \ + 0, \ + LWS_ARRAY_SIZE(map), \ + LSMT_SCHEMA \ + } + +typedef struct lws_struct_serialize_st { + const struct lws_dll2 *dllpos; + const lws_struct_map_t *map; + const char *obj; + size_t map_entries; + size_t map_entry; + size_t size; + char subsequent; + char idt; +} lws_struct_serialize_st_t; + +enum { + LSSERJ_FLAG_PRETTY = 1 +}; + +typedef struct lws_struct_serialize { + lws_struct_serialize_st_t st[LEJP_MAX_PARSING_STACK_DEPTH]; + + size_t offset; + size_t remaining; + + int sp; + int flags; +} lws_struct_serialize_t; + +typedef enum { + LSJS_RESULT_CONTINUE, + LSJS_RESULT_FINISH, + LSJS_RESULT_ERROR +} lws_struct_json_serialize_result_t; + +LWS_VISIBLE LWS_EXTERN int +lws_struct_json_init_parse(struct lejp_ctx *ctx, lejp_callback cb, + void *user); + +LWS_VISIBLE LWS_EXTERN signed char +lws_struct_schema_only_lejp_cb(struct lejp_ctx *ctx, char reason); + +LWS_VISIBLE LWS_EXTERN signed char +lws_struct_default_lejp_cb(struct lejp_ctx *ctx, char reason); + +LWS_VISIBLE LWS_EXTERN lws_struct_serialize_t * +lws_struct_json_serialize_create(const lws_struct_map_t *map, + size_t map_entries, int flags, void *ptoplevel); + +LWS_VISIBLE LWS_EXTERN void +lws_struct_json_serialize_destroy(lws_struct_serialize_t **pjs); + +LWS_VISIBLE LWS_EXTERN lws_struct_json_serialize_result_t +lws_struct_json_serialize(lws_struct_serialize_t *js, uint8_t *buf, + size_t len, size_t *written); + +#if defined(LWS_WITH_STRUCT_SQLITE3) + +LWS_VISIBLE LWS_EXTERN int +lws_struct_sq3_deserialize(sqlite3 *pdb, const lws_struct_map_t *schema, + lws_dll2_owner_t *o, struct lwsac **ac, + uint64_t start, int limit); + +LWS_VISIBLE LWS_EXTERN int +lws_struct_sq3_create_table(sqlite3 *pdb, const lws_struct_map_t *schema); + +LWS_VISIBLE LWS_EXTERN int +lws_struct_sq3_open(struct lws_context *context, const char *sqlite3_path, + sqlite3 **pdb); + +LWS_VISIBLE LWS_EXTERN int +lws_struct_sq3_close(sqlite3 **pdb); + +#endif diff --git a/lib/core/buflist.c b/lib/core/buflist.c new file mode 100644 index 0000000000000000000000000000000000000000..6d8190fac821c5b8bbe5ca3641b55810b2068855 --- /dev/null +++ b/lib/core/buflist.c @@ -0,0 +1,170 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +/* lws_buflist */ + +int +lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf, + size_t len) +{ + struct lws_buflist *nbuf; + int first = !*head; + void *p = *head; + int sanity = 1024; + + assert(buf); + assert(len); + + /* append at the tail */ + while (*head) { + if (!--sanity) { + lwsl_err("%s: buflist reached sanity limit\n", __func__); + return -1; + } + if (*head == (*head)->next) { + lwsl_err("%s: corrupt list points to self\n", __func__); + return -1; + } + head = &((*head)->next); + } + + lwsl_info("%s: len %u first %d %p\n", __func__, (uint32_t)len, first, p); + + nbuf = (struct lws_buflist *)lws_malloc(sizeof(**head) + len, __func__); + if (!nbuf) { + lwsl_err("%s: OOM\n", __func__); + return -1; + } + + nbuf->len = len; + nbuf->pos = 0; + nbuf->next = NULL; + + p = (void *)nbuf->buf; + memcpy(p, buf, len); + + *head = nbuf; + + return first; /* returns 1 if first segment just created */ +} + +static int +lws_buflist_destroy_segment(struct lws_buflist **head) +{ + struct lws_buflist *old = *head; + + assert(*head); + *head = old->next; + old->next = NULL; + lws_free(old); + + return !*head; /* returns 1 if last segment just destroyed */ +} + +void +lws_buflist_destroy_all_segments(struct lws_buflist **head) +{ + struct lws_buflist *p = *head, *p1; + + while (p) { + p1 = p->next; + p->next = NULL; + lws_free(p); + p = p1; + } + + *head = NULL; +} + +size_t +lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf) +{ + if (!*head) { + if (buf) + *buf = NULL; + + return 0; + } + + if (!(*head)->len && (*head)->next) + lws_buflist_destroy_segment(head); + + if (!*head) { + if (buf) + *buf = NULL; + + return 0; + } + + assert((*head)->pos < (*head)->len); + + if (buf) + *buf = (*head)->buf + (*head)->pos; + + return (*head)->len - (*head)->pos; +} + +int +lws_buflist_use_segment(struct lws_buflist **head, size_t len) +{ + assert(*head); + assert(len); + assert((*head)->pos + len <= (*head)->len); + + (*head)->pos += len; + if ((*head)->pos == (*head)->len) + lws_buflist_destroy_segment(head); + + if (!*head) + return 0; + + return (int)((*head)->len - (*head)->pos); +} + +void +lws_buflist_describe(struct lws_buflist **head, void *id) +{ + struct lws_buflist *old; + int n = 0; + + if (*head == NULL) + lwsl_notice("%p: buflist empty\n", id); + + while (*head) { + lwsl_notice("%p: %d: %llu / %llu (%llu left)\n", id, n, + (unsigned long long)(*head)->pos, + (unsigned long long)(*head)->len, + (unsigned long long)(*head)->len - (*head)->pos); + old = *head; + head = &((*head)->next); + if (*head == old) { + lwsl_err("%s: next points to self\n", __func__); + break; + } + n++; + } +} diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index 6582566b562b4c5541b6a8f56f8372040c9cc189..9791ae19475ead01847e39f9b566e3c0b9925771 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -94,321 +94,6 @@ int lws_open(const char *__file, int __oflag, ...) #endif -void -lws_dll_add_head(struct lws_dll *d, struct lws_dll *phead) -{ - if (!lws_dll_is_detached(d, phead)) { - assert(0); /* only wholly detached things can be added */ - return; - } - - /* our next guy is current first guy, if any */ - if (phead->next != d) - d->next = phead->next; - - /* if there is a next guy, set his prev ptr to our next ptr */ - if (d->next) - d->next->prev = d; - /* there is nobody previous to us, we are the head */ - d->prev = NULL; - - /* set the first guy to be us */ - phead->next = d; - - /* if there was nothing on the list before, we are also now the tail */ - if (!phead->prev) - phead->prev = d; - - assert(d->prev != d); - assert(d->next != d); -} - -void -lws_dll_add_tail(struct lws_dll *d, struct lws_dll *phead) -{ - if (!lws_dll_is_detached(d, phead)) { - assert(0); /* only wholly detached things can be added */ - return; - } - - /* our previous guy is current last guy */ - d->prev = phead->prev; - /* if there is a prev guy, set his next ptr to our prev ptr */ - if (d->prev) - d->prev->next = d; - /* our next ptr is NULL */ - d->next = NULL; - /* set the last guy to be us */ - phead->prev = d; - - /* list head is also us if we're the first */ - if (!phead->next) - phead->next = d; - - assert(d->prev != d); - assert(d->next != d); -} - -void -lws_dll_insert(struct lws_dll *n, struct lws_dll *target, - struct lws_dll *phead, int before) -{ - if (!lws_dll_is_detached(n, phead)) { - assert(0); /* only wholly detached things can be inserted */ - return; - } - if (!target) { - /* - * the case where there's no target identified degenerates to - * a simple add at head or tail - */ - if (before) { - lws_dll_add_head(n, phead); - return; - } - lws_dll_add_tail(n, phead); - return; - } - - /* - * in the case there's a target "cursor", we have to do the work to - * stitch the new guy in appropriately - */ - - if (before) { - /* - * we go before dd - * DDp <-> DD <-> DDn --> DDp <-> us <-> DD <-> DDn - */ - /* we point forward to dd */ - n->next = target; - /* we point back to what dd used to point back to */ - n->prev = target->prev; - /* DDp points forward to us now */ - if (target->prev) - target->prev->next = n; - /* DD points back to us now */ - target->prev = n; - - /* if target was the head, we are now the head */ - if (phead->next == target) - phead->next = n; - - /* since we are before another guy, we cannot become the tail */ - - } else { - /* - * we go after dd - * DDp <-> DD <-> DDn --> DDp <-> DD <-> us <-> DDn - */ - /* we point forward to what dd used to point forward to */ - n->next = target->next; - /* we point back to dd */ - n->prev = target; - /* DDn points back to us */ - if (target->next) - target->next->prev = n; - /* DD points forward to us */ - target->next = n; - - /* if target was the tail, we are now the tail */ - if (phead->prev == target) - phead->prev = n; - - /* since we go after another guy, we cannot become the head */ - } -} - -/* situation is: - * - * HEAD: struct lws_dll * = &entry1 - * - * Entry 1: struct lws_dll .pprev = &HEAD , .next = Entry 2 - * Entry 2: struct lws_dll .pprev = &entry1 , .next = &entry2 - * Entry 3: struct lws_dll .pprev = &entry2 , .next = NULL - * - * Delete Entry1: - * - * - HEAD = &entry2 - * - Entry2: .pprev = &HEAD, .next = &entry3 - * - Entry3: .pprev = &entry2, .next = NULL - * - * Delete Entry2: - * - * - HEAD = &entry1 - * - Entry1: .pprev = &HEAD, .next = &entry3 - * - Entry3: .pprev = &entry1, .next = NULL - * - * Delete Entry3: - * - * - HEAD = &entry1 - * - Entry1: .pprev = &HEAD, .next = &entry2 - * - Entry2: .pprev = &entry1, .next = NULL - * - */ - -void -lws_dll_remove(struct lws_dll *d) -{ - if (!d->prev && !d->next) - return; - - /* - * remove us - * - * USp <-> us <-> USn --> USp <-> USn - */ - - /* if we have a next guy, set his prev to our prev */ - if (d->next) - d->next->prev = d->prev; - - /* set our prev guy to our next guy instead of us */ - if (d->prev) - d->prev->next = d->next; - - /* we're out of the list, we should not point anywhere any more */ - d->prev = NULL; - d->next = NULL; -} - -void -lws_dll_remove_track_tail(struct lws_dll *d, struct lws_dll *phead) -{ - if (lws_dll_is_detached(d, phead)) { - assert(phead->prev != d); - assert(phead->next != d); - return; - } - - /* if we have a next guy, set his prev to our prev */ - if (d->next) - d->next->prev = d->prev; - - /* if we have a previous guy, set his next to our next */ - if (d->prev) - d->prev->next = d->next; - - if (phead->prev == d) - phead->prev = d->prev; - - if (phead->next == d) - phead->next = d->next; - - /* we're out of the list, we should not point anywhere any more */ - d->prev = NULL; - d->next = NULL; -} - - -int -lws_dll_foreach_safe(struct lws_dll *phead, void *user, - int (*cb)(struct lws_dll *d, void *user)) -{ - lws_start_foreach_dll_safe(struct lws_dll *, p, tp, phead->next) { - if (cb(p, user)) - return 1; - } lws_end_foreach_dll_safe(p, tp); - - return 0; -} - -int -lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user, - int (*cb)(struct lws_dll2 *d, void *user)) -{ - lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, owner->head) { - if (cb(p, user)) - return 1; - } lws_end_foreach_dll_safe(p, tp); - - return 0; -} - -void -lws_dll2_add_head(struct lws_dll2 *d, struct lws_dll2_owner *owner) -{ - if (!lws_dll2_is_detached(d)) { - assert(0); /* only wholly detached things can be added */ - return; - } - - /* our next guy is current first guy, if any */ - if (owner->head != d) - d->next = owner->head; - - /* if there is a next guy, set his prev ptr to our next ptr */ - if (d->next) - d->next->prev = d; - /* there is nobody previous to us, we are the head */ - d->prev = NULL; - - /* set the first guy to be us */ - owner->head = d; - - if (!owner->tail) - owner->tail = d; - - d->owner = owner; - owner->count++; -} - -void -lws_dll2_add_tail(struct lws_dll2 *d, struct lws_dll2_owner *owner) -{ - if (!lws_dll2_is_detached(d)) { - assert(0); /* only wholly detached things can be added */ - return; - } - - /* our previous guy is current last guy */ - d->prev = owner->tail; - /* if there is a prev guy, set his next ptr to our prev ptr */ - if (d->prev) - d->prev->next = d; - /* our next ptr is NULL */ - d->next = NULL; - /* set the last guy to be us */ - owner->tail = d; - - /* list head is also us if we're the first */ - if (!owner->head) - owner->head = d; - - d->owner = owner; - owner->count++; -} - -void -lws_dll2_remove(struct lws_dll2 *d) -{ - if (lws_dll2_is_detached(d)) - return; - - /* if we have a next guy, set his prev to our prev */ - if (d->next) - d->next->prev = d->prev; - - /* if we have a previous guy, set his next to our next */ - if (d->prev) - d->prev->next = d->next; - - /* if we have phead, track the tail and head if it points to us... */ - - if (d->owner->tail == d) - d->owner->tail = d->prev; - - if (d->owner->head == d) - d->owner->head = d->next; - - d->owner->count--; - - /* we're out of the list, we should not point anywhere any more */ - d->owner = NULL; - d->prev = NULL; - d->next = NULL; -} - #if !(defined(LWS_PLAT_OPTEE) && !defined(LWS_WITH_NETWORK)) LWS_VISIBLE lws_usec_t @@ -441,151 +126,6 @@ lws_pthread_self_to_tsi(struct lws_context *context) #endif } - -/* lws_buflist */ - -int -lws_buflist_append_segment(struct lws_buflist **head, const uint8_t *buf, - size_t len) -{ - struct lws_buflist *nbuf; - int first = !*head; - void *p = *head; - int sanity = 1024; - - assert(buf); - assert(len); - - /* append at the tail */ - while (*head) { - if (!--sanity) { - lwsl_err("%s: buflist reached sanity limit\n", __func__); - return -1; - } - if (*head == (*head)->next) { - lwsl_err("%s: corrupt list points to self\n", __func__); - return -1; - } - head = &((*head)->next); - } - - lwsl_info("%s: len %u first %d %p\n", __func__, (uint32_t)len, first, p); - - nbuf = (struct lws_buflist *)lws_malloc(sizeof(**head) + len, __func__); - if (!nbuf) { - lwsl_err("%s: OOM\n", __func__); - return -1; - } - - nbuf->len = len; - nbuf->pos = 0; - nbuf->next = NULL; - - p = (void *)nbuf->buf; - memcpy(p, buf, len); - - *head = nbuf; - - return first; /* returns 1 if first segment just created */ -} - -static int -lws_buflist_destroy_segment(struct lws_buflist **head) -{ - struct lws_buflist *old = *head; - - assert(*head); - *head = old->next; - old->next = NULL; - lws_free(old); - - return !*head; /* returns 1 if last segment just destroyed */ -} - -void -lws_buflist_destroy_all_segments(struct lws_buflist **head) -{ - struct lws_buflist *p = *head, *p1; - - while (p) { - p1 = p->next; - p->next = NULL; - lws_free(p); - p = p1; - } - - *head = NULL; -} - -size_t -lws_buflist_next_segment_len(struct lws_buflist **head, uint8_t **buf) -{ - if (!*head) { - if (buf) - *buf = NULL; - - return 0; - } - - if (!(*head)->len && (*head)->next) - lws_buflist_destroy_segment(head); - - if (!*head) { - if (buf) - *buf = NULL; - - return 0; - } - - assert((*head)->pos < (*head)->len); - - if (buf) - *buf = (*head)->buf + (*head)->pos; - - return (*head)->len - (*head)->pos; -} - -int -lws_buflist_use_segment(struct lws_buflist **head, size_t len) -{ - assert(*head); - assert(len); - assert((*head)->pos + len <= (*head)->len); - - (*head)->pos += len; - if ((*head)->pos == (*head)->len) - lws_buflist_destroy_segment(head); - - if (!*head) - return 0; - - return (int)((*head)->len - (*head)->pos); -} - -void -lws_buflist_describe(struct lws_buflist **head, void *id) -{ - struct lws_buflist *old; - int n = 0; - - if (*head == NULL) - lwsl_notice("%p: buflist empty\n", id); - - while (*head) { - lwsl_notice("%p: %d: %llu / %llu (%llu left)\n", id, n, - (unsigned long long)(*head)->pos, - (unsigned long long)(*head)->len, - (unsigned long long)(*head)->len - (*head)->pos); - old = *head; - head = &((*head)->next); - if (*head == old) { - lwsl_err("%s: next points to self\n", __func__); - break; - } - n++; - } -} - LWS_EXTERN void * lws_context_user(struct lws_context *context) { diff --git a/lib/core/lws_dll.c b/lib/core/lws_dll.c new file mode 100644 index 0000000000000000000000000000000000000000..bbb9c2b26c03b27c8a2d980f58ffcf51521e56a5 --- /dev/null +++ b/lib/core/lws_dll.c @@ -0,0 +1,246 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + + +void +lws_dll_add_head(struct lws_dll *d, struct lws_dll *phead) +{ + if (!lws_dll_is_detached(d, phead)) { + assert(0); /* only wholly detached things can be added */ + return; + } + + /* our next guy is current first guy, if any */ + if (phead->next != d) + d->next = phead->next; + + /* if there is a next guy, set his prev ptr to our next ptr */ + if (d->next) + d->next->prev = d; + /* there is nobody previous to us, we are the head */ + d->prev = NULL; + + /* set the first guy to be us */ + phead->next = d; + + /* if there was nothing on the list before, we are also now the tail */ + if (!phead->prev) + phead->prev = d; + + assert(d->prev != d); + assert(d->next != d); +} + +void +lws_dll_add_tail(struct lws_dll *d, struct lws_dll *phead) +{ + if (!lws_dll_is_detached(d, phead)) { + assert(0); /* only wholly detached things can be added */ + return; + } + + /* our previous guy is current last guy */ + d->prev = phead->prev; + /* if there is a prev guy, set his next ptr to our prev ptr */ + if (d->prev) + d->prev->next = d; + /* our next ptr is NULL */ + d->next = NULL; + /* set the last guy to be us */ + phead->prev = d; + + /* list head is also us if we're the first */ + if (!phead->next) + phead->next = d; + + assert(d->prev != d); + assert(d->next != d); +} + +void +lws_dll_insert(struct lws_dll *n, struct lws_dll *target, + struct lws_dll *phead, int before) +{ + if (!lws_dll_is_detached(n, phead)) { + assert(0); /* only wholly detached things can be inserted */ + return; + } + if (!target) { + /* + * the case where there's no target identified degenerates to + * a simple add at head or tail + */ + if (before) { + lws_dll_add_head(n, phead); + return; + } + lws_dll_add_tail(n, phead); + return; + } + + /* + * in the case there's a target "cursor", we have to do the work to + * stitch the new guy in appropriately + */ + + if (before) { + /* + * we go before dd + * DDp <-> DD <-> DDn --> DDp <-> us <-> DD <-> DDn + */ + /* we point forward to dd */ + n->next = target; + /* we point back to what dd used to point back to */ + n->prev = target->prev; + /* DDp points forward to us now */ + if (target->prev) + target->prev->next = n; + /* DD points back to us now */ + target->prev = n; + + /* if target was the head, we are now the head */ + if (phead->next == target) + phead->next = n; + + /* since we are before another guy, we cannot become the tail */ + + } else { + /* + * we go after dd + * DDp <-> DD <-> DDn --> DDp <-> DD <-> us <-> DDn + */ + /* we point forward to what dd used to point forward to */ + n->next = target->next; + /* we point back to dd */ + n->prev = target; + /* DDn points back to us */ + if (target->next) + target->next->prev = n; + /* DD points forward to us */ + target->next = n; + + /* if target was the tail, we are now the tail */ + if (phead->prev == target) + phead->prev = n; + + /* since we go after another guy, we cannot become the head */ + } +} + +/* situation is: + * + * HEAD: struct lws_dll * = &entry1 + * + * Entry 1: struct lws_dll .pprev = &HEAD , .next = Entry 2 + * Entry 2: struct lws_dll .pprev = &entry1 , .next = &entry2 + * Entry 3: struct lws_dll .pprev = &entry2 , .next = NULL + * + * Delete Entry1: + * + * - HEAD = &entry2 + * - Entry2: .pprev = &HEAD, .next = &entry3 + * - Entry3: .pprev = &entry2, .next = NULL + * + * Delete Entry2: + * + * - HEAD = &entry1 + * - Entry1: .pprev = &HEAD, .next = &entry3 + * - Entry3: .pprev = &entry1, .next = NULL + * + * Delete Entry3: + * + * - HEAD = &entry1 + * - Entry1: .pprev = &HEAD, .next = &entry2 + * - Entry2: .pprev = &entry1, .next = NULL + * + */ + +void +lws_dll_remove(struct lws_dll *d) +{ + if (!d->prev && !d->next) + return; + + /* + * remove us + * + * USp <-> us <-> USn --> USp <-> USn + */ + + /* if we have a next guy, set his prev to our prev */ + if (d->next) + d->next->prev = d->prev; + + /* set our prev guy to our next guy instead of us */ + if (d->prev) + d->prev->next = d->next; + + /* we're out of the list, we should not point anywhere any more */ + d->prev = NULL; + d->next = NULL; +} + +void +lws_dll_remove_track_tail(struct lws_dll *d, struct lws_dll *phead) +{ + if (lws_dll_is_detached(d, phead)) { + assert(phead->prev != d); + assert(phead->next != d); + return; + } + + /* if we have a next guy, set his prev to our prev */ + if (d->next) + d->next->prev = d->prev; + + /* if we have a previous guy, set his next to our next */ + if (d->prev) + d->prev->next = d->next; + + if (phead->prev == d) + phead->prev = d->prev; + + if (phead->next == d) + phead->next = d->next; + + /* we're out of the list, we should not point anywhere any more */ + d->prev = NULL; + d->next = NULL; +} + + +int +lws_dll_foreach_safe(struct lws_dll *phead, void *user, + int (*cb)(struct lws_dll *d, void *user)) +{ + lws_start_foreach_dll_safe(struct lws_dll *, p, tp, phead->next) { + if (cb(p, user)) + return 1; + } lws_end_foreach_dll_safe(p, tp); + + return 0; +} diff --git a/lib/core/lws_dll2.c b/lib/core/lws_dll2.c new file mode 100644 index 0000000000000000000000000000000000000000..dad96bb38269b5acde8d045cab0a2108bf1b61c2 --- /dev/null +++ b/lib/core/lws_dll2.c @@ -0,0 +1,138 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +#ifdef LWS_HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif + +int +lws_dll2_foreach_safe(struct lws_dll2_owner *owner, void *user, + int (*cb)(struct lws_dll2 *d, void *user)) +{ + lws_start_foreach_dll_safe(struct lws_dll2 *, p, tp, owner->head) { + if (cb(p, user)) + return 1; + } lws_end_foreach_dll_safe(p, tp); + + return 0; +} + +void +lws_dll2_add_head(struct lws_dll2 *d, struct lws_dll2_owner *owner) +{ + if (!lws_dll2_is_detached(d)) { + assert(0); /* only wholly detached things can be added */ + return; + } + + /* our next guy is current first guy, if any */ + if (owner->head != d) + d->next = owner->head; + + /* if there is a next guy, set his prev ptr to our next ptr */ + if (d->next) + d->next->prev = d; + /* there is nobody previous to us, we are the head */ + d->prev = NULL; + + /* set the first guy to be us */ + owner->head = d; + + if (!owner->tail) + owner->tail = d; + + d->owner = owner; + owner->count++; +} + +void +lws_dll2_add_tail(struct lws_dll2 *d, struct lws_dll2_owner *owner) +{ + if (!lws_dll2_is_detached(d)) { + assert(0); /* only wholly detached things can be added */ + return; + } + + /* our previous guy is current last guy */ + d->prev = owner->tail; + /* if there is a prev guy, set his next ptr to our prev ptr */ + if (d->prev) + d->prev->next = d; + /* our next ptr is NULL */ + d->next = NULL; + /* set the last guy to be us */ + owner->tail = d; + + /* list head is also us if we're the first */ + if (!owner->head) + owner->head = d; + + d->owner = owner; + owner->count++; +} + +void +lws_dll2_remove(struct lws_dll2 *d) +{ + if (lws_dll2_is_detached(d)) + return; + + /* if we have a next guy, set his prev to our prev */ + if (d->next) + d->next->prev = d->prev; + + /* if we have a previous guy, set his next to our next */ + if (d->prev) + d->prev->next = d->next; + + /* if we have phead, track the tail and head if it points to us... */ + + if (d->owner->tail == d) + d->owner->tail = d->prev; + + if (d->owner->head == d) + d->owner->head = d->next; + + d->owner->count--; + + /* we're out of the list, we should not point anywhere any more */ + d->owner = NULL; + d->prev = NULL; + d->next = NULL; +} + +void +lws_dll2_clear(struct lws_dll2 *d) +{ + d->owner = NULL; + d->prev = NULL; + d->next = NULL; +} + +void +lws_dll2_owner_clear(struct lws_dll2_owner *d) +{ + d->head = NULL; + d->tail = NULL; + d->count = 0; +} diff --git a/lib/jose/jws/jose.c b/lib/jose/jws/jose.c index 2cb337be104fe24a189f640affa23ff0d4c0597f..627fd237a857ef9845a8515fa1f1cdd3c7e26f69 100644 --- a/lib/jose/jws/jose.c +++ b/lib/jose/jws/jose.c @@ -176,7 +176,8 @@ lws_jws_jose_cb(struct lejp_ctx *ctx, char reason) lejp_check_path_match(&args->jwk_jctx); if (args->jwk_jctx.path_match) - args->jwk_jctx.callback(&args->jwk_jctx, reason); + args->jwk_jctx.pst[args->jwk_jctx.pst_sp]. + callback(&args->jwk_jctx, reason); } // lwsl_notice("%s: %s %d (%d)\n", __func__, ctx->path, reason, ctx->sp); diff --git a/lib/misc/lejp.c b/lib/misc/lejp.c index 370126b6df33ec83f5b21f6525ba2a831ad71222..9f974479ec628d3812406e0bf98a0a600f502078 100644 --- a/lib/misc/lejp.c +++ b/lib/misc/lejp.c @@ -74,15 +74,20 @@ lejp_construct(struct lejp_ctx *ctx, ctx->st[0].b = 0; ctx->sp = 0; ctx->ipos = 0; - ctx->ppos = 0; ctx->path_match = 0; + ctx->path_stride = 0; ctx->path[0] = '\0'; - ctx->callback = callback; ctx->user = user; - ctx->paths = paths; - ctx->count_paths = count_paths; ctx->line = 1; - ctx->callback(ctx, LEJPCB_CONSTRUCTED); + + 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); } /** @@ -99,7 +104,7 @@ void lejp_destruct(struct lejp_ctx *ctx) { /* no allocations... just let callback know what it happening */ - ctx->callback(ctx, LEJPCB_DESTRUCTED); + ctx->pst[0].callback(ctx, LEJPCB_DESTRUCTED); } /** @@ -128,23 +133,29 @@ void lejp_change_callback(struct lejp_ctx *ctx, signed char (*callback)(struct lejp_ctx *ctx, char reason)) { - ctx->callback(ctx, LEJPCB_DESTRUCTED); - ctx->callback = callback; - ctx->callback(ctx, LEJPCB_CONSTRUCTED); - ctx->callback(ctx, LEJPCB_START); + 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; + int n, 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->count_paths; n++) { + for (n = 0; !ctx->path_match && + n < ctx->pst[ctx->pst_sp].count_paths; n++) { ctx->wildcount = 0; p = ctx->path; - q = ctx->paths[n]; + + q = *((char **)(((char *)ctx->pst[ctx->pst_sp].paths) + (n * s))); + while (*p && *q) { if (*q != '*') { if (*p != *q) @@ -170,7 +181,7 @@ lejp_check_path_match(struct lejp_ctx *ctx) continue; ctx->path_match = n + 1; - ctx->path_match_len = ctx->ppos; + ctx->path_match_len = ctx->pst[ctx->pst_sp].ppos; return; } @@ -188,7 +199,7 @@ lejp_get_wildcard(struct lejp_ctx *ctx, int wildcard, char *dest, int len) n = ctx->wild[wildcard]; - while (--len && n < ctx->ppos && + while (--len && n < ctx->pst[ctx->pst_sp].ppos && (n == ctx->wild[wildcard] || ctx->path[n] != '.')) *dest++ = ctx->path[n++]; @@ -222,8 +233,8 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) static const char esc_tran[] = "\"\\/\b\f\n\r\t"; static const char tokens[] = "rue alse ull "; - if (!ctx->sp && !ctx->ppos) - ctx->callback(ctx, LEJPCB_START); + if (!ctx->sp && !ctx->pst[ctx->pst_sp].ppos) + ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_START); while (len--) { c = *json++; @@ -252,7 +263,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) ret = LEJP_REJECT_IDLE_NO_BRACE; goto reject; } - if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_OBJECT_START)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -284,7 +295,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) } if (ctx->st[ctx->sp - 1].s != LEJP_MP_DELIM) { ctx->buf[ctx->npos] = '\0'; - if (ctx->callback(ctx, + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_STR_END) < 0) { ret = LEJP_REJECT_CALLBACK; goto reject; @@ -391,10 +402,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) goto reject; } ctx->st[ctx->sp].s = LEJP_MP_VALUE; - ctx->path[ctx->ppos] = '\0'; + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; lejp_check_path_match(ctx); - if (ctx->callback(ctx, LEJPCB_PAIR_NAME)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_PAIR_NAME)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -415,7 +426,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) c = LEJP_MP_STRING; ctx->npos = 0; ctx->buf[0] = '\0'; - if (ctx->callback(ctx, LEJPCB_VAL_STR_START)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_STR_START)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -426,7 +437,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; c = LEJP_MEMBERS; lejp_check_path_match(ctx); - if (ctx->callback(ctx, LEJPCB_OBJECT_START)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_OBJECT_START)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -437,10 +448,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) /* push */ ctx->st[ctx->sp].s = LEJP_MP_ARRAY_END; c = LEJP_MP_VALUE; - ctx->path[ctx->ppos++] = '['; - ctx->path[ctx->ppos++] = ']'; - ctx->path[ctx->ppos] = '\0'; - if (ctx->callback(ctx, LEJPCB_ARRAY_START)) { + 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->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_START)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -464,12 +475,12 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) } /* drop the path [n] bit */ if (ctx->sp) { - ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->pst[ctx->pst_sp].ppos = ctx->st[ctx->sp - 1].p; ctx->ipos = ctx->st[ctx->sp - 1].i; } - ctx->path[ctx->ppos] = '\0'; + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; if (ctx->path_match && - ctx->ppos <= ctx->path_match_len) + ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len) /* * we shrank the path to be * smaller than the matching point @@ -544,12 +555,12 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) ctx->buf[ctx->npos] = '\0'; if (ctx->f & LEJP_SEEN_POINT) { - if (ctx->callback(ctx, LEJPCB_VAL_NUM_FLOAT)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_NUM_FLOAT)) { ret = LEJP_REJECT_CALLBACK; goto reject; } } else { - if (ctx->callback(ctx, LEJPCB_VAL_NUM_INT)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_NUM_INT)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -580,7 +591,7 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) case 3: ctx->buf[0] = '1'; ctx->buf[1] = '\0'; - if (ctx->callback(ctx, LEJPCB_VAL_TRUE)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_TRUE)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -588,14 +599,14 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) case 8: ctx->buf[0] = '0'; ctx->buf[1] = '\0'; - if (ctx->callback(ctx, LEJPCB_VAL_FALSE)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_FALSE)) { ret = LEJP_REJECT_CALLBACK; goto reject; } break; case 12: ctx->buf[0] = '\0'; - if (ctx->callback(ctx, LEJPCB_VAL_NULL)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_NULL)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -605,12 +616,12 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) break; case LEJP_MP_COMMA_OR_END: - ctx->path[ctx->ppos] = '\0'; + 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->sp) { - ctx->ppos = 0; + ctx->pst[ctx->pst_sp].ppos = 0; /* * since we came back to root level, * no path can still match @@ -618,10 +629,10 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) ctx->path_match = 0; break; } - ctx->ppos = ctx->st[ctx->sp - 1].p; - ctx->path[ctx->ppos] = '\0'; + ctx->pst[ctx->pst_sp].ppos = ctx->st[ctx->sp - 1].p; + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; if (ctx->path_match && - ctx->ppos <= ctx->path_match_len) + ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len) /* * we shrank the path to be * smaller than the matching point @@ -649,12 +660,12 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) } /* drop the path [n] bit */ if (ctx->sp) { - ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->pst[ctx->pst_sp].ppos = ctx->st[ctx->sp - 1].p; ctx->ipos = ctx->st[ctx->sp - 1].i; } - ctx->path[ctx->ppos] = '\0'; + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; if (ctx->path_match && - ctx->ppos <= ctx->path_match_len) + ctx->pst[ctx->pst_sp].ppos <= ctx->path_match_len) /* * we shrank the path to be * smaller than the matching point @@ -667,11 +678,11 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) if (c == '}') { if (!ctx->sp) { lejp_check_path_match(ctx); - if (ctx->callback(ctx, LEJPCB_OBJECT_END)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_OBJECT_END)) { ret = LEJP_REJECT_CALLBACK; goto reject; } - if (ctx->callback(ctx, LEJPCB_COMPLETE)) + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_COMPLETE)) goto reject; else /* done, return unused amount */ @@ -680,19 +691,19 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) /* pop */ ctx->sp--; if (ctx->sp) { - ctx->ppos = ctx->st[ctx->sp - 1].p; + ctx->pst[ctx->pst_sp].ppos = ctx->st[ctx->sp - 1].p; ctx->ipos = ctx->st[ctx->sp - 1].i; } - ctx->path[ctx->ppos] = '\0'; + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; if (ctx->path_match && - ctx->ppos <= ctx->path_match_len) + 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->callback(ctx, LEJPCB_OBJECT_END)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_OBJECT_END)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -704,15 +715,15 @@ lejp_parse(struct lejp_ctx *ctx, const unsigned char *json, int len) case LEJP_MP_ARRAY_END: array_end: - ctx->path[ctx->ppos] = '\0'; + 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->ppos = ctx->st[ctx->sp - 1].p; - ctx->path[ctx->ppos] = '\0'; + ctx->pst[ctx->pst_sp].ppos = ctx->st[ctx->sp - 1].p; + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; break; } if (c != ']') { @@ -721,7 +732,7 @@ array_end: } ctx->st[ctx->sp].s = LEJP_MP_COMMA_OR_END; - ctx->callback(ctx, LEJPCB_ARRAY_END); + ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_ARRAY_END); break; } @@ -732,7 +743,7 @@ emit_string_char: /* assemble the string value into chunks */ ctx->buf[ctx->npos++] = c; if (ctx->npos == sizeof(ctx->buf) - 1) { - if (ctx->callback(ctx, LEJPCB_VAL_STR_CHUNK)) { + if (ctx->pst[ctx->pst_sp].callback(ctx, LEJPCB_VAL_STR_CHUNK)) { ret = LEJP_REJECT_CALLBACK; goto reject; } @@ -741,22 +752,22 @@ emit_string_char: continue; } /* name part of name:value pair */ - ctx->path[ctx->ppos++] = c; + ctx->path[ctx->pst[ctx->pst_sp].ppos++] = c; continue; add_stack_level: /* push on to the object stack */ - if (ctx->ppos && ctx->st[ctx->sp].s != LEJP_MP_COMMA_OR_END && + 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->ppos++] = '.'; + ctx->path[ctx->pst[ctx->pst_sp].ppos++] = '.'; - ctx->st[ctx->sp].p = ctx->ppos; + ctx->st[ctx->sp].p = ctx->pst[ctx->pst_sp].ppos; ctx->st[ctx->sp].i = ctx->ipos; if (++ctx->sp == LWS_ARRAY_SIZE(ctx->st)) { ret = LEJP_REJECT_STACK_OVERFLOW; goto reject; } - ctx->path[ctx->ppos] = '\0'; + ctx->path[ctx->pst[ctx->pst_sp].ppos] = '\0'; ctx->st[ctx->sp].s = c; ctx->st[ctx->sp].b = 0; continue; @@ -777,10 +788,55 @@ redo_character: return LEJP_CONTINUE; reject: - ctx->callback(ctx, LEJPCB_FAILED); + 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) { diff --git a/lib/misc/lws-struct-lejp.c b/lib/misc/lws-struct-lejp.c new file mode 100644 index 0000000000000000000000000000000000000000..af637cc3b37d4a77f77bca227ca3cbdcf8bca601 --- /dev/null +++ b/lib/misc/lws-struct-lejp.c @@ -0,0 +1,762 @@ +/* + * libwebsockets - lws_struct JSON serialization helpers + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <libwebsockets.h> +#include <core/private.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]; + int n = a->map_entries_st[ctx->pst_sp]; + lejp_callback cb = map->lejp_cb; + + if (reason != LEJPCB_VAL_STR_END || ctx->path_match != 1) + return 0; + + while (n--) { + if (strcmp(ctx->buf, map->colname)) { + map++; + continue; + } + + 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 (!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; + + 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; + char *u; + int n; + + if (reason == LEJPCB_ARRAY_END) { + lejp_parser_pop(ctx); + + return 0; + } + + if (reason == LEJPCB_ARRAY_START) { + map = &args->map_st[ctx->pst_sp][ctx->path_match - 1]; + n = args->map_entries_st[ctx->pst_sp]; + + 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]; + map = &args->map_st[ctx->pst_sp][ctx->path_match - 1]; + n = args->map_entries_st[ctx->pst_sp]; + + if (reason == LEJPCB_OBJECT_START) { + + if (map->type != LSMT_CHILD_PTR) { + ctx->pst[ctx->pst_sp].user = NULL; + + return 0; + } + pmap = map; + + lws_struct_lejp_push(ctx, args, map, NULL); + map = &args->map_st[ctx->pst_sp][ctx->path_match - 1]; + n = args->map_entries_st[ctx->pst_sp]; + } + + if (reason == LEJPCB_OBJECT_END && pmap && pmap->type == LSMT_CHILD_PTR) + lejp_parser_pop(ctx); + + if (map->type == LSMT_SCHEMA) { + + while (n--) { + if (strcmp(map->colname, ctx->buf)) { + 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\n", __func__); + + 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 (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_notice("%s: created child object size %d\n", __func__, + (int)pmap->aux); + + if (pmap->type == LSMT_LIST) { + list = (struct lws_dll2 *)((char *)ctx->pst[ctx->pst_sp].user + + map->ofs_clist); + + lws_dll2_add_tail(list, owner); + } + } + + 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 = 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 = atoi(ctx->buf); + break; + } + if (map->aux == sizeof(unsigned int)) { + unsigned int *pi; + pi = (unsigned int *)(u + map->ofs); + *pi = atoi(ctx->buf); + break; + } + if (map->aux == sizeof(unsigned long)) { + unsigned long *pl; + pl = (unsigned long *)(u + map->ofs); + *pl = atol(ctx->buf); + } else { + unsigned long long *pll; + pll = (unsigned long long *)(u + map->ofs); + *pll = 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 = 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 = 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); + } + 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) +{ + 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, + 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; + struct lws_dll2_owner *o; + unsigned long long uli; + const char *q; + const void *p; + char dbuf[72]; + long long li; + int n; + + *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_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; + + 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 -= 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 = lws_snprintf(dbuf, sizeof(dbuf), + "%s", uli ? "true" : "false"); + } else + budget = 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 = lws_snprintf(dbuf, sizeof(dbuf), "%lld", li); + break; + + case LSMT_STRING_CHAR_ARRAY: + budget = strlen(q); + if (!js->offset) { + *buf++ = '\"'; + len--; + } + break; + + case LSMT_STRING_PTR: + budget = strlen(q); + 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 = 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 tto handle parsing child members */ + + n = j->idt; + j = &js->st[++js->sp]; + j->idt = 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); + budget = lws_snprintf(dbuf, 15, "\"schema\":"); + if (js->flags & LSSERJ_FLAG_PRETTY) + dbuf[budget++] = ' '; + + budget += 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; + /* we're actually at the same level */ + j->subsequent = 1; + j->idt = 1; + break; + } + + 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; + + 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 nd 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; +} diff --git a/lib/misc/lws-struct-sqlite.c b/lib/misc/lws-struct-sqlite.c new file mode 100644 index 0000000000000000000000000000000000000000..2ed2a4ef1bf1cf5a85b2371acd0ef9588e5d3734 --- /dev/null +++ b/lib/misc/lws-struct-sqlite.c @@ -0,0 +1,275 @@ +/* + * libwebsockets - lws_struct JSON serialization helpers + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <libwebsockets.h> +#include <core/private.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; + const lws_struct_map_t *map = a->map_st[0]; + int n, mems = a->map_entries_st[0]; + lws_dll2_owner_t *o = (lws_dll2_owner_t *)a->cb_arg; + char *u = lwsac_use_zero(&a->ac, a->dest_len, a->ac_block_size); + 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 = atoi(cv[n]); + break; + } + if (map->aux == sizeof(int)) { + int *pi; + pi = (int *)(u + map->ofs); + *pi = atoi(cv[n]); + break; + } + if (map->aux == sizeof(long)) { + long *pl; + pl = (long *)(u + map->ofs); + *pl = atol(cv[n]); + 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 = atoi(cv[n]); + break; + } + if (map->aux == sizeof(unsigned int)) { + unsigned int *pi; + pi = (unsigned int *)(u + map->ofs); + *pi = atoi(cv[n]); + break; + } + if (map->aux == sizeof(unsigned long)) { + unsigned long *pl; + pl = (unsigned long *)(u + map->ofs); + *pl = atol(cv[n]); + break; + } + { + unsigned long long *pll; + pll = (unsigned long long *)(u + map->ofs); + *pll = 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 = li; + } + break; + + case LSMT_STRING_CHAR_ARRAY: + s = (char *)(u + map->ofs); + lim = map->aux - 1; + 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 lws_struct_map_t *schema, + lws_dll2_owner_t *o, struct lwsac **ac, + uint64_t start, int limit) +{ + char s[150], where[32]; + lws_struct_args_t a; + + memset(&a, 0, sizeof(a)); + 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); + + where[0] = '\0'; + if (start) + lws_snprintf(where, sizeof(where), " where when < %llu ", + (unsigned long long)start); + + lws_snprintf(s, sizeof(s) - 1, "select * " + "from %s %s order by created desc limit %d;", + schema->colname, where, limit); + + if (sqlite3_exec(pdb, s, lws_struct_sq3_deser_cb, &a, NULL) != SQLITE_OK) { + lwsl_err("%s: fail\n", sqlite3_errmsg(pdb)); + lwsac_free(&a.ac); + return -1; + } + + *ac = a.ac; + + 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 = schema->child_map_size, subsequent = 0; + char s[2048], *p = s, *end = &s[sizeof(s) - 1], *pri = "primary key"; + + p += lws_snprintf(p, end - p, "create table if not exists %s (", + schema->colname); + + while (map_size--) { + if (map->type > LSMT_STRING_PTR) { + map++; + continue; + } + if (subsequent && (end - p) > 3) + *p++ = ','; + subsequent = 1; + if (map->type < LSMT_STRING_CHAR_ARRAY) + p += lws_snprintf(p, end - p, "%s integer %s", + map->colname, pri); + else + p += lws_snprintf(p, end - p, "%s varchar %s", + map->colname, pri); + pri = ""; + map++; + } + + p += lws_snprintf(p, 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, + sqlite3 **pdb) +{ + int uid = 0, gid = 0; + + if (sqlite3_open_v2(sqlite3_path, pdb, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, + NULL) != SQLITE_OK) { + lwsl_err("%s: Unable to open db %s: %s\n", + __func__, sqlite3_path, sqlite3_errmsg(*pdb)); + + return 1; + } + + lws_get_effective_uid_gid(context, &uid, &gid); + if (uid) + chown(sqlite3_path, uid, gid); + chmod(sqlite3_path, 0600); + + sqlite3_extended_result_codes(*pdb, 1); + + return 0; +} + +int +lws_struct_sq3_close(sqlite3 **pdb) +{ + if (!*pdb) + return 0; + + sqlite3_close(*pdb); + *pdb = NULL; + + return 0; +} diff --git a/lib/misc/lwsac/lwsac.c b/lib/misc/lwsac/lwsac.c index a401bbc7f6c218fcbf3460625a4c2fd209878e86..6471c1652ee9d4f1059b92471ab602f5d3134c55 100644 --- a/lib/misc/lwsac/lwsac.c +++ b/lib/misc/lwsac/lwsac.c @@ -158,6 +158,7 @@ lwsac_free(struct lwsac **head) { struct lwsac *it = *head; + *head = NULL; lwsl_debug("%s: head %p\n", __func__, *head); while (it) { @@ -166,14 +167,15 @@ lwsac_free(struct lwsac **head) free(it); it = tmp; } - - *head = NULL; } void lwsac_info(struct lwsac *head) { - lwsl_debug("%s: lac %p: %dKiB in %d blocks\n", __func__, head, + if (!head) + lwsl_debug("%s: empty\n", __func__); + else + lwsl_debug("%s: lac %p: %dKiB in %d blocks\n", __func__, head, (int)(head->total_alloc_size >> 10), head->total_blocks); } diff --git a/minimal-examples/api-tests/README.md b/minimal-examples/api-tests/README.md index ff8d48ed370965d4fc140e6f42d65e8b1316d06f..8a1477d3b52ac4aa32316da8c3d226c7f7a1680d 100644 --- a/minimal-examples/api-tests/README.md +++ b/minimal-examples/api-tests/README.md @@ -3,6 +3,7 @@ These are buildable test apps that run in CI to confirm correct api operation. |name|tests| ---|--- api-test-lwsac|LWS Allocated Chunks api +api-test-lws_struct-json|Selftests for lws_struct JSON serialization and deserialization api-test-lws_tokenize|Generic secure string tokenizer api api-test-fts|LWS Full-text Search api api-test-gencrypto|LWS Generic Crypto apis diff --git a/minimal-examples/api-tests/api-test-lws_struct-json/CMakeLists.txt b/minimal-examples/api-tests/api-test-lws_struct-json/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a0900f866b2530e039d41b28b48c0dabb2543d57 --- /dev/null +++ b/minimal-examples/api-tests/api-test-lws_struct-json/CMakeLists.txt @@ -0,0 +1,73 @@ +cmake_minimum_required(VERSION 2.8) +include(CheckCSourceCompiles) + +set(SAMP lws-api-test-lws_struct-json) +set(SRCS main.c) + +# If we are being built as part of lws, confirm current build config supports +# reqconfig, else skip building ourselves. +# +# If we are being built externally, confirm installed lws was configured to +# support reqconfig, else error out with a helpful message about the problem. +# +MACRO(require_lws_config reqconfig _val result) + + if (DEFINED ${reqconfig}) + if (${reqconfig}) + set (rq 1) + else() + set (rq 0) + endif() + else() + set(rq 0) + endif() + + if (${_val} EQUAL ${rq}) + set(SAME 1) + else() + set(SAME 0) + endif() + + if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME}) + if (${_val}) + message("${SAMP}: skipping as lws being built without ${reqconfig}") + else() + message("${SAMP}: skipping as lws built with ${reqconfig}") + endif() + set(${result} 0) + else() + if (LWS_WITH_MINIMAL_EXAMPLES) + set(MET ${SAME}) + else() + CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig}) + if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig}) + set(HAS_${reqconfig} 0) + else() + set(HAS_${reqconfig} 1) + endif() + if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val})) + set(MET 1) + else() + set(MET 0) + endif() + endif() + if (NOT MET) + if (${_val}) + message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}") + else() + message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project") + endif() + endif() + endif() +ENDMACRO() + + + + add_executable(${SAMP} ${SRCS}) + + if (websockets_shared) + target_link_libraries(${SAMP} websockets_shared) + add_dependencies(${SAMP} websockets_shared) + else() + target_link_libraries(${SAMP} websockets) + endif() diff --git a/minimal-examples/api-tests/api-test-lws_struct-json/README.md b/minimal-examples/api-tests/api-test-lws_struct-json/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ebe930d24f2e11b7a815c875552b82b465e731ad --- /dev/null +++ b/minimal-examples/api-tests/api-test-lws_struct-json/README.md @@ -0,0 +1,56 @@ +# lws api test lws_struct JSON + +Demonstrates how to use and performs selftests for lws_struct +JSON serialization and deserialization + +## build + +``` + $ cmake . && make +``` + +## usage + +Commandline option|Meaning +---|--- +-d <loglevel>|Debug verbosity in decimal, eg, -d15 + +``` + $ ./lws-api-test-lws_struct-json +[2019/03/30 22:09:09:2529] USER: LWS API selftest: lws_struct JSON +[2019/03/30 22:09:09:2625] NOTICE: main: ++++++++++++++++ test 1 +[2019/03/30 22:09:09:2812] NOTICE: builder.hostname = 'learn', timeout = 1800, targets (2) +[2019/03/30 22:09:09:2822] NOTICE: target.name 'target1' (target 0x543a830) +[2019/03/30 22:09:09:2824] NOTICE: target.name 'target2' (target 0x543a860) +[2019/03/30 22:09:09:2826] NOTICE: main: .... strarting serialization of test 1 +[2019/03/30 22:09:09:2899] NOTICE: ser says 1 +{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":1800,"targets":[{"name":"target1"},{"name":"target2"}]} +[2019/03/30 22:09:09:2929] NOTICE: main: ++++++++++++++++ test 2 +[2019/03/30 22:09:09:2932] NOTICE: builder.hostname = 'learn', timeout = 0, targets (3) +[2019/03/30 22:09:09:2932] NOTICE: target.name 'target1' (target 0x543b060) +[2019/03/30 22:09:09:2933] NOTICE: target.name 'target2' (target 0x543b090) +[2019/03/30 22:09:09:2933] NOTICE: target.name 'target3' (target 0x543b0c0) +[2019/03/30 22:09:09:2934] NOTICE: main: .... strarting serialization of test 2 +[2019/03/30 22:09:09:2935] NOTICE: ser says 1 +{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":0,"targets":[{"name":"target1"},{"name":"target2"},{"name":"target3"}]} +[2019/03/30 22:09:09:2940] NOTICE: main: ++++++++++++++++ test 3 +[2019/03/30 22:09:09:2959] NOTICE: builder.hostname = 'learn', timeout = 1800, targets (2) +[2019/03/30 22:09:09:2960] NOTICE: target.name 'target1' (target 0x543b450) +[2019/03/30 22:09:09:2961] NOTICE: child 0x543b480, target.child.somename 'abc' +[2019/03/30 22:09:09:2961] NOTICE: target.name 'target2' (target 0x543b490) +[2019/03/30 22:09:09:2962] NOTICE: main: .... strarting serialization of test 3 +[2019/03/30 22:09:09:2969] NOTICE: ser says 1 +{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":1800,"targets":[{"name":"target1","child":{"somename":"abc"}},{"name":"target2"}]} +[2019/03/30 22:09:09:2970] NOTICE: main: ++++++++++++++++ test 4 +[2019/03/30 22:09:09:2971] NOTICE: builder.hostname = 'learn', timeout = 1800, targets (0) +[2019/03/30 22:09:09:2971] NOTICE: main: .... strarting serialization of test 4 +[2019/03/30 22:09:09:2973] NOTICE: ser says 1 +{"schema":"com-warmcat-sai-builder","hostname":"learn","nspawn_timeout":1800} +[2019/03/30 22:09:09:2974] NOTICE: main: ++++++++++++++++ test 5 +[2019/03/30 22:09:09:2978] NOTICE: builder.hostname = '', timeout = 0, targets (0) +[2019/03/30 22:09:09:2979] NOTICE: main: .... strarting serialization of test 5 +[2019/03/30 22:09:09:2980] NOTICE: ser says 1 +{"schema":"com-warmcat-sai-builder","hostname":"","nspawn_timeout":0} +[2019/03/30 22:09:09:2982] USER: Completed: PASS +``` + diff --git a/minimal-examples/api-tests/api-test-lws_struct-json/main.c b/minimal-examples/api-tests/api-test-lws_struct-json/main.c new file mode 100644 index 0000000000000000000000000000000000000000..98c7d93ad9503f3a4ce40d5df71df392058b5509 --- /dev/null +++ b/minimal-examples/api-tests/api-test-lws_struct-json/main.c @@ -0,0 +1,365 @@ +/* + * lws-api-test-lws_struct-json + * + * Copyright (C) 2019 Andy Green <andy@warmcat.com> + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * lws_struct apis are used to serialize and deserialize your C structs and + * linked-lists in a standardized way that's very modest on memory but + * convenient and easy to maintain. + * + * The API test shows how to serialize and deserialize a struct with a linked- + * list of child structs in JSON using lws_struct APIs. + */ + +#include <libwebsockets.h> + +/* + * in this example, the JSON is for one "builder" object, which may specify + * a child list "targets" of zero or more "target" objects. + */ + +static const char * const json_tests[] = { + "{" /* test 1 */ + "\"schema\":\"com-warmcat-sai-builder\"," + + "\"hostname\":\"learn\"," + "\"nspawn_timeout\":1800," + "\"targets\":[" + "{" + "\"name\":\"target1\"," + "\"someflag\":true" + "}," + "{" + "\"name\":\"target2\"," + "\"someflag\":false" + "}" + "]" + "}", + "{" /* test 2 */ + "\"schema\":\"com-warmcat-sai-builder\"," + + "\"hostname\":\"learn\"," + "\"targets\":[" + "{" + "\"name\":\"target1\"" + "}," + "{" + "\"name\":\"target2\"" + "}," + "{" + "\"name\":\"target3\"" + "}" + "]" + "}", "{" /* test 3 */ + "\"schema\":\"com-warmcat-sai-builder\"," + + "\"hostname\":\"learn\"," + "\"nspawn_timeout\":1800," + "\"targets\":[" + "{" + "\"name\":\"target1\"," + "\"unrecognized\":\"xyz\"," + "\"child\": {" + "\"somename\": \"abc\"," + "\"junk\": { \"x\": \"y\" }" + "}" + "}," + "{" + "\"name\":\"target2\"" + "}" + "]" + "}", + "{" /* test 4 */ + "\"schema\":\"com-warmcat-sai-builder\"," + + "\"hostname\":\"learn\"," + "\"nspawn_timeout\":1800" + "}", + "{" /* test 5 */ + "\"schema\":\"com-warmcat-sai-builder\"" + "}", + "{" /* test 6 ... check huge strings into smaller fixed char array */ + "\"schema\":\"com-warmcat-sai-builder\"," + "\"hostname\":\"" + "PYvtan6kqppjnS0KpYTCaiOLsJkc7XecAr1kcE0aCIciewYB+JcLG82mO1Vb1mJtjDwUjBxy2I6A" + "zefzoWUWmqZbsv4MXR55j9bKlyz1liiSX63iO0x6JAwACMtE2MkgcLwR86TSWAD9D1QKIWqg5RJ/" + "CRuVsW0DKAUMD52ql4JmPFuJpJgTq28z6PhYNzN3yI3bmQt6bzhA+A/xAsFzSBnb3MHYWzGMprr5" + "3FAP1ISo5Ec9i+2ehV40sG6Q470sH3PGQZ0YRPO7Sh/SyrSQ/scONmxRc3AcXl7X/CSs417ii+CV" + "8sq3ZgcxKNB7tNfN7idNx3upZ00G2BZy9jSy03cLKKLNaNUt0TQsxXbH55uDHzSEeZWvxJgT6zB1" + "NoMhdC02w+oXim94M6z6COCnqT3rgkGk8PHMry9Bkh4yVpRmzIRfMmln/lEhdZgxky2+g5hhlSIG" + "JYDCrdynD9kCfvfy6KGOpNIi1X+mhbbWn4lnL9ZKihL/RrfOV+oV4R26IDq+KqUiJBENeo8/GXkG" + "LUH/87iPyzXKEMavr6fkrK0vTGto8yEYxmOyaVz8phG5rwf4jJgmYNoMbGo8gWvhqO7UAGy2g7MW" + "v+B/t1eZZ+1euLsNrWAsFJiFbQKgdFfQT3RjB14iU8knlQ8usoy+pXssY2ddGJGVcGC21oZvstK9" + "eu1eRZftda/wP+N5unT1Hw7kCoVzqxHieiYt47EGIOaaQ7XjZDK6qPN6O/grHnvJZm2vBkxuXgsY" + "VkRQ7AuTWIecphqFsq7Wbc1YNbMW47SVU5zMD0WaCqbaaI0t4uIzRvPlD8cpiiTzFTrEHlIBTf8/" + "uZjjEGGLhJR1jPqA9D1Ej3ChV+ye6F9JTUMlozRMsGuF8U4btDzH5xdnmvRS4Ar6LKEtAXGkj2yu" + "yJln+v4RIWj2xOGPJovOqiXwi0FyM61f8U8gj0OiNA2/QlvrqQVDF7sMXgjvaE7iQt5vMETteZlx" + "+z3f+jTFM/aon511W4+ZkRD+6AHwucvM9BEC\"" + "}", + "{" /* test 7 ... check huge strings into char * */ + "\"schema\":\"com-warmcat-sai-builder\"," + "\"targets\":[" + "{" + "\"name\":\"" + "PYvtan6kqppjnS0KpYTCaiOLsJkc7XecAr1kcE0aCIciewYB+JcLG82mO1Vb1mJtjDwUjBxy2I6A" + "zefzoWUWmqZbsv4MXR55j9bKlyz1liiSX63iO0x6JAwACMtE2MkgcLwR86TSWAD9D1QKIWqg5RJ/" + "CRuVsW0DKAUMD52ql4JmPFuJpJgTq28z6PhYNzN3yI3bmQt6bzhA+A/xAsFzSBnb3MHYWzGMprr5" + "3FAP1ISo5Ec9i+2ehV40sG6Q470sH3PGQZ0YRPO7Sh/SyrSQ/scONmxRc3AcXl7X/CSs417ii+CV" + "8sq3ZgcxKNB7tNfN7idNx3upZ00G2BZy9jSy03cLKKLNaNUt0TQsxXbH55uDHzSEeZWvxJgT6zB1" + "NoMhdC02w+oXim94M6z6COCnqT3rgkGk8PHMry9Bkh4yVpRmzIRfMmln/lEhdZgxky2+g5hhlSIG" + "JYDCrdynD9kCfvfy6KGOpNIi1X+mhbbWn4lnL9ZKihL/RrfOV+oV4R26IDq+KqUiJBENeo8/GXkG" + "LUH/87iPyzXKEMavr6fkrK0vTGto8yEYxmOyaVz8phG5rwf4jJgmYNoMbGo8gWvhqO7UAGy2g7MW" + "v+B/t1eZZ+1euLsNrWAsFJiFbQKgdFfQT3RjB14iU8knlQ8usoy+pXssY2ddGJGVcGC21oZvstK9" + "eu1eRZftda/wP+N5unT1Hw7kCoVzqxHieiYt47EGIOaaQ7XjZDK6qPN6O/grHnvJZm2vBkxuXgsY" + "VkRQ7AuTWIecphqFsq7Wbc1YNbMW47SVU5zMD0WaCqbaaI0t4uIzRvPlD8cpiiTzFTrEHlIBTf8/" + "uZjjEGGLhJR1jPqA9D1Ej3ChV+ye6F9JTUMlozRMsGuF8U4btDzH5xdnmvRS4Ar6LKEtAXGkj2yu" + "yJln+v4RIWj2xOGPJovOqiXwi0FyM61f8U8gj0OiNA2/QlvrqQVDF7sMXgjvaE7iQt5vMETteZlx" + "+z3f+jTFM/aon511W4+ZkRD+6AHwucvM9BEC\"}]}" + "}", +}; + +/* + * These are the expected outputs for each test, without pretty formatting. + * + * There are some differences to do with missing elements being rendered with + * default values. + */ + +static const char * const json_expected[] = { + "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"learn\"," + "\"nspawn_timeout\":1800,\"targets\":[{\"name\":\"target1\",\"someflag\":true}," + "{\"name\":\"target2\",\"someflag\":false}]}", + + "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"learn\"," + "\"nspawn_timeout\":0,\"targets\":[{\"name\":\"target1\",\"someflag\":false}," + "{\"name\":\"target2\",\"someflag\":false},{\"name\":\"target3\",\"someflag\":false}]}", + + "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"learn\"," + "\"nspawn_timeout\":1800,\"targets\":[{\"name\":\"target1\",\"someflag\":false," + "\"child\":{\"somename\":\"abc\"}},{\"name\":\"target2\",\"someflag\":false}]}", + + "{\"schema\":\"com-warmcat-sai-builder\"," + "\"hostname\":\"learn\",\"nspawn_timeout\":1800}", + + "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"\"," + "\"nspawn_timeout\":0}", + + "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":" + "\"PYvtan6kqppjnS0KpYTCaiOLsJkc7Xe\"," + "\"nspawn_timeout\":0}", + + "{\"schema\":\"com-warmcat-sai-builder\",\"hostname\":\"\"," + "\"nspawn_timeout\":0,\"targets\":[{\"name\":\"PYvtan6kqppjnS0KpYTC" + "aiOLsJkc7XecAr1kcE0aCIciewYB+JcLG82mO1Vb1mJtjDwUjBxy2I6Azefz" + "oWUWmqZbsv4MXR55j9bKlyz1liiSX63iO0x6JAwACMtE2MkgcLwR86TSWAD9" + "D1QKIWqg5RJ/CRuVsW0DKAUMD52ql4JmPFuJpJgTq28z6PhYNzN3yI3bmQt6" + "bzhA+A/xAsFzSBnb3MHYWzGMprr53FAP1ISo5Ec9i+2ehV40sG6Q470sH3PG" + "QZ0YRPO7Sh/SyrSQ/scONmxRc3AcXl7X/CSs417ii+CV8sq3ZgcxKNB7tNfN" + "7idNx3upZ00G2BZy9jSy03cLKKLNaNUt0TQsxXbH55uDHzSEeZWvxJgT6zB1" + "NoMhdC02w+oXim94M6z6COCnqT3rgkGk8PHMry9Bkh4yVpRmzIRfMmln/lEh" + "dZgxky2+g5hhlSIGJYDCrdynD9kCfvfy6KGOpNIi1X+mhbbWn4lnL9ZKihL/" + "RrfOV+oV4R26IDq+KqUiJBENeo8/GXkGLUH/87iPyzXKEMavr6fkrK0vTGto" + "8yEYxmOyaVz8phG5rwf4jJgmYNoMbGo8gWvhqO7UAGy2g7MWv+B/t1eZZ+1e" + "uLsNrWAsFJiFbQKgdFfQT3RjB14iU8knlQ8usoy+pXssY2ddGJGVcGC21oZv" + "stK9eu1eRZftda/wP+N5unT1Hw7kCoVzqxHieiYt47EGIOaaQ7XjZDK6qPN6" + "O/grHnvJZm2vBkxuXgsYVkRQ7AuTWIecphqFsq7Wbc1YNbMW47SVU5zMD0Wa" + "CqbaaI0t4uIzRvPlD8cpiiTzFTrEHlIBTf8/uZjjEGGLhJR1jPqA9D1Ej3Ch" + "V+ye6F9JTUMlozRMsGuF8U4btDzH5xdnmvRS4Ar6LKEtAXGkj2yuyJln+v4R" + "IWj2xOGPJovOqiXwi0FyM61f8U8gj0OiNA2/QlvrqQVDF7sMXgjvaE7iQt5v" + "METteZlx+z3f+jTFM/aon511W4+ZkRD+6AHwucvM9BEC\"" + ",\"someflag\":false}]}" +}; + +/* + * These annotate the members in the struct that will be serialized and + * deserialized with type and size information, as well as the name to use + * in the serialization format. + * + * Struct members that aren't annotated like this won't be serialized and + * when the struct is created during deserialiation, the will be set to 0 + * or NULL. + */ + +/* child object */ + +typedef struct sai_child { + const char * somename; +} sai_child_t; + +lws_struct_map_t lsm_child[] = { /* describes serializable members */ + LSM_STRING_PTR (sai_child_t, somename, "somename"), +}; + +/* target object */ + +typedef struct sai_target { + struct lws_dll2 target_list; + sai_child_t * child; + + const char * name; + char someflag; +} sai_target_t; + +static const lws_struct_map_t lsm_target[] = { + LSM_STRING_PTR (sai_target_t, name, "name"), + LSM_BOOLEAN (sai_target_t, someflag, "someflag"), + LSM_CHILD_PTR (sai_target_t, child, sai_child_t, + NULL, lsm_child, "child"), +}; + +/* builder object */ + +typedef struct sai_builder { + struct lws_dll2_owner targets; + + char hostname[32]; + unsigned int nspawn_timeout; +} sai_builder_t; + +static const lws_struct_map_t lsm_builder[] = { + LSM_CARRAY (sai_builder_t, hostname, "hostname"), + LSM_UNSIGNED (sai_builder_t, nspawn_timeout, "nspawn_timeout"), + LSM_LIST (sai_builder_t, targets, + sai_target_t, target_list, + NULL, lsm_target, "targets"), +}; + +/* Schema table + * + * Before we can understand the serialization top level format, we must read + * the schema, use the table below to create the right toplevel object for the + * schema name, and select the correct map tables to interpret the rest of the + * serialization. + * + * Therefore the schema tables below are the starting point for the + * JSON deserialization. + */ + +static const lws_struct_map_t lsm_schema_map[] = { + LSM_SCHEMA (sai_builder_t, NULL, + lsm_builder, "com-warmcat-sai-builder"), +}; + +static int +show_target(struct lws_dll2 *d, void *user) +{ + sai_target_t *t = lws_container_of(d, sai_target_t, target_list); + + lwsl_notice(" target.name '%s' (target %p)\n", t->name, t); + + if (t->child) + lwsl_notice(" child %p, target.child.somename '%s'\n", + t->child, t->child->somename); + + return 0; +} + + +int main(int argc, const char **argv) +{ + int n, m, e = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; +#if 1 + lws_struct_serialize_t *ser; + uint8_t buf[4096]; + size_t written; +#endif + struct lejp_ctx ctx; + lws_struct_args_t a; + sai_builder_t *b; + const char *p; + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS API selftest: lws_struct JSON\n"); + + for (m = 0; m < (int)LWS_ARRAY_SIZE(json_tests); m++) { + + /* 1. deserialize the canned JSON into structs */ + + lwsl_notice("%s: ++++++++++++++++ test %d\n", __func__, m + 1); + + memset(&a, 0, sizeof(a)); + a.map_st[0] = lsm_schema_map; + a.map_entries_st[0] = LWS_ARRAY_SIZE(lsm_schema_map); + a.ac_block_size = 512; + + lws_struct_json_init_parse(&ctx, NULL, &a); + n = (int)(signed char)lejp_parse(&ctx, (uint8_t *)json_tests[m], + strlen(json_tests[m])); + if (n < 0) { + lwsl_err("%s: notification JSON decode failed '%s'\n", + __func__, lejp_error_to_string(n)); + e++; + goto done; + } + lwsac_info(a.ac); + + b = a.dest; + if (!b) { + lwsl_err("%s: didn't produce any output\n", __func__); + e++; + goto done; + } + + lwsl_notice("builder.hostname = '%s', timeout = %d, targets (%d)\n", + b->hostname, b->nspawn_timeout, + b->targets.count); + + lws_dll2_foreach_safe(&b->targets, NULL, show_target); + + /* 2. serialize the structs into JSON and confirm */ + + lwsl_notice("%s: .... strarting serialization of test %d\n", + __func__, m + 1); + ser = lws_struct_json_serialize_create(lsm_schema_map, + LWS_ARRAY_SIZE(lsm_schema_map), + 0//LSSERJ_FLAG_PRETTY + , b); + if (!ser) { + lwsl_err("%s: unable to init serialization\n", __func__); + goto bail; + } + + do { + n = lws_struct_json_serialize(ser, buf, sizeof(buf), + &written); + lwsl_notice("ser says %d\n", n); + switch (n) { + case LSJS_RESULT_CONTINUE: + case LSJS_RESULT_FINISH: + puts((const char *)buf); + break; + case LSJS_RESULT_ERROR: + goto bail; + } + } while(n == LSJS_RESULT_CONTINUE); + + if (strcmp(json_expected[m], (char *)buf)) { + lwsl_err("%s: test %d: expected %s\n", __func__, m + 1, + json_expected[m]); + e++; + } + + lws_struct_json_serialize_destroy(&ser); + +done: + lwsac_free(&a.ac); + } + + if (e) + goto bail; + + lwsl_user("Completed: PASS\n"); + + return 0; + +bail: + lwsl_user("Completed: FAIL\n"); + + return 1; +} diff --git a/minimal-examples/api-tests/api-test-lws_struct-json/selftest.sh b/minimal-examples/api-tests/api-test-lws_struct-json/selftest.sh new file mode 100755 index 0000000000000000000000000000000000000000..16d1e2e8e463dec610c1cc4c2c7cc22f3cf0b14f --- /dev/null +++ b/minimal-examples/api-tests/api-test-lws_struct-json/selftest.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# $1: path to minimal example binaries... +# if lws is built with -DLWS_WITH_MINIMAL_EXAMPLES=1 +# that will be ./bin from your build dir +# +# $2: path for logs and results. The results will go +# in a subdir named after the directory this script +# is in +# +# $3: offset for test index count +# +# $4: total test count +# +# $5: path to ./minimal-examples dir in lws +# +# Test return code 0: OK, 254: timed out, other: error indication + +. $5/selftests-library.sh + +COUNT_TESTS=1 + +dotest $1 $2 apiselftest +exit $FAILS