From 599927a1a7e74a23e02d9670bee5a910363320f3 Mon Sep 17 00:00:00 2001 From: armink Date: Thu, 8 Oct 2020 11:17:36 +0800 Subject: [PATCH] [dcos] add more documents. --- README.md | 2 +- docs/.nojekyll | 0 docs/_coverpage.md | 15 + docs/_media/favicon.ico | Bin 0 -> 12154 bytes docs/_media/flashdb.png | Bin 0 -> 22462 bytes docs/_media/flashdb_framework.png | Bin 0 -> 15686 bytes docs/_media/flashdb_logo.png | Bin 0 -> 12154 bytes docs/_media/flashdb_porting_layer.png | Bin 0 -> 22319 bytes docs/{zh/images => _media}/wechat_support.png | Bin docs/_navbar.md | 3 + docs/_scripts/docsify.min.js | 1 + docs/_scripts/emoji.min.js | 1 + docs/_scripts/prism-c.min.js | 1 + docs/_scripts/search.min.js | 1 + docs/_sidebar.md | 18 + docs/api.md | 347 ++++++++ docs/configuration.md | 41 + docs/demo-stm32f103ve.md | 35 + docs/demo-stm32f405rg-spi-flash.md | 40 + docs/demo-stm32f405rg.md | 35 + docs/en/api.md | 1 - docs/en/readme.md | 3 - docs/index.html | 65 ++ docs/porting.md | 143 +++ docs/quick-started.md | 61 ++ docs/readme.md | 77 +- docs/sample-kvdb-basic.md | 60 ++ docs/sample-kvdb-traversal.md | 38 + docs/sample-kvdb-type-blob.md | 71 ++ docs/sample-kvdb-type-string.md | 69 ++ docs/sample-tsdb-basic.md | 169 ++++ docs/vue.css | 829 ++++++++++++++++++ docs/zh-cn/README.md | 79 ++ docs/zh-cn/_coverpage.md | 15 + docs/zh-cn/_media/flashdb_framework.png | Bin 0 -> 14300 bytes docs/zh-cn/_media/flashdb_porting_layer.png | Bin 0 -> 19238 bytes docs/zh-cn/_media/wechat_support.png | Bin 0 -> 18611 bytes docs/zh-cn/_navbar.md | 3 + docs/zh-cn/_sidebar.md | 19 + docs/zh-cn/api.md | 347 ++++++++ docs/zh-cn/configuration.md | 41 + docs/zh-cn/demo-details.md | 209 +++++ docs/zh-cn/demo-stm32f103ve.md | 36 + docs/zh-cn/demo-stm32f405rg-spi-flash.md | 42 + docs/zh-cn/demo-stm32f405rg.md | 37 + docs/zh-cn/porting.md | 145 +++ docs/zh-cn/quick-started.md | 63 ++ docs/zh-cn/sample-kvdb-basic.md | 60 ++ docs/zh-cn/sample-kvdb-traversal.md | 38 + docs/zh-cn/sample-kvdb-type-blob.md | 71 ++ docs/zh-cn/sample-kvdb-type-string.md | 69 ++ docs/zh-cn/sample-tsdb-basic.md | 169 ++++ docs/zh/api.md | 5 - docs/zh/design.md | 3 - docs/zh/port.md | 5 - docs/zh/readme.md | 5 - 56 files changed, 3560 insertions(+), 27 deletions(-) create mode 100644 docs/.nojekyll create mode 100644 docs/_coverpage.md create mode 100644 docs/_media/favicon.ico create mode 100644 docs/_media/flashdb.png create mode 100644 docs/_media/flashdb_framework.png create mode 100644 docs/_media/flashdb_logo.png create mode 100644 docs/_media/flashdb_porting_layer.png rename docs/{zh/images => _media}/wechat_support.png (100%) create mode 100644 docs/_navbar.md create mode 100644 docs/_scripts/docsify.min.js create mode 100644 docs/_scripts/emoji.min.js create mode 100644 docs/_scripts/prism-c.min.js create mode 100644 docs/_scripts/search.min.js create mode 100644 docs/_sidebar.md create mode 100644 docs/api.md create mode 100644 docs/configuration.md create mode 100644 docs/demo-stm32f103ve.md create mode 100644 docs/demo-stm32f405rg-spi-flash.md create mode 100644 docs/demo-stm32f405rg.md delete mode 100644 docs/en/api.md delete mode 100644 docs/en/readme.md create mode 100644 docs/index.html create mode 100644 docs/porting.md create mode 100644 docs/quick-started.md create mode 100644 docs/sample-kvdb-basic.md create mode 100644 docs/sample-kvdb-traversal.md create mode 100644 docs/sample-kvdb-type-blob.md create mode 100644 docs/sample-kvdb-type-string.md create mode 100644 docs/sample-tsdb-basic.md create mode 100644 docs/vue.css create mode 100644 docs/zh-cn/README.md create mode 100644 docs/zh-cn/_coverpage.md create mode 100644 docs/zh-cn/_media/flashdb_framework.png create mode 100644 docs/zh-cn/_media/flashdb_porting_layer.png create mode 100644 docs/zh-cn/_media/wechat_support.png create mode 100644 docs/zh-cn/_navbar.md create mode 100644 docs/zh-cn/_sidebar.md create mode 100644 docs/zh-cn/api.md create mode 100644 docs/zh-cn/configuration.md create mode 100644 docs/zh-cn/demo-details.md create mode 100644 docs/zh-cn/demo-stm32f103ve.md create mode 100644 docs/zh-cn/demo-stm32f405rg-spi-flash.md create mode 100644 docs/zh-cn/demo-stm32f405rg.md create mode 100644 docs/zh-cn/porting.md create mode 100644 docs/zh-cn/quick-started.md create mode 100644 docs/zh-cn/sample-kvdb-basic.md create mode 100644 docs/zh-cn/sample-kvdb-traversal.md create mode 100644 docs/zh-cn/sample-kvdb-type-blob.md create mode 100644 docs/zh-cn/sample-kvdb-type-string.md create mode 100644 docs/zh-cn/sample-tsdb-basic.md delete mode 100644 docs/zh/api.md delete mode 100644 docs/zh/design.md delete mode 100644 docs/zh/port.md delete mode 100644 docs/zh/readme.md diff --git a/README.md b/README.md index 155dd3e..92b4285 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ FlashDB 提供了主要功能的示例,直接加入工程即可运行,并具 ## 支持 - ![support](docs/zh/images/wechat_support.png) + ![support](docs/_media/wechat_support.png) 如果 FlashDB 解决了你的问题,不妨扫描上面二维码请我 **喝杯咖啡**~ diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/_coverpage.md b/docs/_coverpage.md new file mode 100644 index 0000000..f2973f6 --- /dev/null +++ b/docs/_coverpage.md @@ -0,0 +1,15 @@ +![FlashDB](_media/flashdb.png) + +> An ultra-lightweight embedded database. + +[![Build Status](https://travis-ci.com/armink/FlashDB.svg?branch=master)](https://travis-ci.com/armink/FlashDB) +[![version](https://img.shields.io/github/release/armink/FlashDB.svg?style=flat-square)](https://github/license/armink/FlashDB/releases/latest) +[![license](https://img.shields.io/github/license/armink/FlashDB)](https://raw.githubusercontent.com/armink/FlashDB/master/LICENSE) + +- Small footprint +- Fast query speed +- Key-Value database +- Time series database + +[GitHub](https://github.com/armink/FlashDB/) +[Getting started](/quick-started) \ No newline at end of file diff --git a/docs/_media/favicon.ico b/docs/_media/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..96414eef0b4277f72814df71e1db00b29527130b GIT binary patch literal 12154 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 literal 0 HcmV?d00001 diff --git a/docs/_media/flashdb.png b/docs/_media/flashdb.png new file mode 100644 index 0000000000000000000000000000000000000000..d3213b8c3ae30bcb25048271c85b6b96597925ad GIT binary patch literal 22462 zcmb4rWmH?u_IC&Yf;$vI|c@s@$H}SCn?9#}x0)wBDJOBwsH-pBj^<=5pDZDr*ra zPZo0(+P(=lf)(RNkmgmlP^*+&v~52R0XZ5MjXaK*z93n)bFBPD*BrhbTDouHaozF+ z|N4#d5zGmC1;jx3Dj6&d;T8Oz()lt5NJD5~FH-}gXaJD2u`5Y$p0S42_mI_*+B-+=ZYiw=Rf~50Ztb~ z{NEVI8Up47Izi%klr8^9*gr&I{C^VRj2rhgECeU0_B&15xY79e-xl28bqH98VEHpX z{>1{aN~C2XE|A^8|0qW=2EBrQ_v1wNzsR2I3ObV^hxUVh@d2ukMr{X801#?`Ad;kq z>ORGZ_T+mFb{E0v^#3~o{gHZ3LUdzus;Dfu>q-5^O$SQz*rT;G!d#f z(FL|5+f8FT5gq9NZ4`ZBRbaI$Oc+|?b?q4|T>G2;m}+61?8md3#-(mr+V z^Ge97p7#G8=mR!_EL>I`BY39hYVggNC<=HT09)y^yemn-hm?}9C5mT|x6kr-ikO%O zmI?hUNM5cNB4@Z)5e4!+d_EM110dl5xYd?fsFdOc#Qd>t>P$@KV9xA_`@gyX0lzHY zN2pv0#TMQsO5#8XnFGLSfoAKec!I(5%Wag*66S`8I^X|gC6)U{s-;8Xn5qj|$)xc{ zeQfl_n5wQ7p`q)XfCd|9yHK9AqI-hLt{Z##fBCQmyf9yxp5IP3skTYHW(rEi0#IIx zdNKfR=xBbqdWj^h?=yLCOxB3md;PoR7dI$X6nd>!bY%Sg_ZJ!*()8)^fvq9{V8o?+ zYn`m}mMTbb>=kQGOiI_kV!eb }(>}xH#D1Bn4Rt5l^0MV0(PqJ9y-T>!Jz??Ii z#%V@O#0S3rQPmCR6#~v67ZKX%0N{oIXM2dNF9F0?!bTg8Te3O;`a}xH;Qp`fLP&6| z@jy>UYS`)5E0Ez^hE4RnHzkW0ahgiRC}5hbi@lG5nM-sHqOrSo*Z8#lrn1B5&+5VLtSSEW6&nwG) zZx!heP7f}*6ktON!4`aoL!AV8Pz2xZ%(L8%ef`K&6Z5Y?x@kstz^yaFQxjhp8p)v; zm94!?JARV_oBFhJH%I_+W&LtN_M@?n<^SG7SSqeH!A3m;!eM=e@}bfD;c^fl)jYqS zSPJgD0e@6=*yuROKqg1`use$aA^u|)!~dgdeMH%+Kkj zDX;+T$lc{?D8dUWPh%@$2G6~pV>Rx`3^Gkclk&H}hLihZ@q;{xH36 zEZ>{U>va4Ba3)7BYiU>fJXlg@*7ILW%@`@!hU0ZH7V2nX$b6(}+#>^kR;l=5k%ruZ zf9V1@x&kN>PP5n7PL&;A@n1el^{&fqzxW)Lui23hVCM&v8uV>({;1IN5jn(1SqhND7 zm?wB~nk;50&|x%_bNxRGDTycpTLUe-m%DuXN_UR?5rE)qmo3OubBimF7}SfjDG5CF5|(a`Cq@7Ck1;0Qbqc3 zmBB$G6K0MC#In@_b6JH@!w}$~K>b&p(BX~%(OxkFd8RVVe-&y+PLdbuX;^yX*!B5Y zidtJi_SG^5lm$eID~0sQnNBJ7HznGCje;@g9j;eVLqZ!WjW0wJ{XBIl+gAwz;*z*` zQ6)48gsCW$5~xdp3Bx{p`S)_aqoq!U-3Mg54YJYof~8Di_<6r>2^kObqZE0%&M(x| z8ELS3_MWW^pa~y5K8~eJj1Lqd;OLn-Yy7}g6J6CvJlLzf7?*Ex2#INY^;*y1KXhcv zNOD3wf2dN@BlL5qypqF0&`1-KzZ&rx-(266wg^1=RCVtXFq#>Xn-FN37>W2|Nhbj~ zErme_`~2+LZmwThqv!z;iN}_Lp~2!Jc~#BRtlR$onBg~HARmFpt+IUBxE}fgoOVaV z2*=Ix4p^%DYdGh7CziSqRqiu?S_4cTbOfKDy3ULxvrD-gNL?}#%Z(&Aw=i_knLj^Q z8hRIGm|wKK-FPc~6)`uAGa3q@#jEe?&Wvi7`iF8Kxm!^hb0s4ZTZVOWq1l+FiAr>OQF?Lz>ESgnz z_Lm~%ZTl$J#2`?0MMq)d;_QPR%afbmer88Rxrzx$p$U=bIrh~YV^WNlX3FE;wI(MN zMW!?f0p4@xnWOsVb#(%)Vs|NB0&90CfQK{sy^3=u67Hcuh7YgvO2Weo50yE@Xlql1bJU#8`b+0GtqBXTsHLOlzWt_U1n(K_V8IBtS-L>U7vlu@3OJFn*71 zm^Kvxw2?$j*zu-gEK_021Axmw7SNwov#r=!+9WgkXYp}HCh&}C_&t0RV;jx>2g#9a zvLUyvZ)Kqp{Jf(N?S~rs9VM=?F5(ZmFel{*tp|Z`IK!`~^)l{cAeLH-Det|)aLUKc z>w+ZM4j9uO$E_PeFYRc^+L)6jbqM4`P8k6`HydE?6+T;qEF4c zB^yYT?pq4KXOK-VpMgXESYG|mC8Rdmzz?TP)M?bW z0j9*ekpjIwkA^eYaym3D;$lf;&D-O7quiMF<@=`*Bpi~fpv?ewXYQ3^!Uo5rnW8&I z*`s~A$(|omvwQD>*b5m#B5(EZ47vnDN^$+unch(BeXgv!U3&G18a|XBU(6y7TQ~5y zmTb{$*}8d}eD!E4u1EY56R~bdvpxzH=1mlaFQsK1F`fM|I3Ot6N^-k9uwSj^2=wJ8 zlWd`_yk6t*1f6(7&2H;W?Qi`xvdt%% zHXFNTjqmdX8!{>!`;9d|>>ZgyFG9?$bLzKeWNHLdnh-jEc?k8YJ%p|xB-#`X&k2b) zdG>ezZm$`fP|JFv!@?V2sY5szieB$^kWGo;9h#UbyNKzFT>!J#j$KeA-uY(^w!;!&>_*i_e zW2|wSxP$WZ&#KSN zC@>Mw>Xcn6qFO32%me93Mm9N<__=)3->8?J=878E|EFHF90`ZTmjF_)W@W)sCiQHXd9fo9ZwyX|Ln9#8Ya@q|_|?{`g|)eUKzK z__t9!oIyZjAW+dw1$acs>74%o*1?N)+ffvEzB0H~T{c*r~ zl03=BhSbyA64w*o44ee7 zU$6D1$!H?bjN+~P_$YQZ6|knW*9QY#o&0DFDc}qw{sS1SC)w7Uw16}9$EAW^lqc>M z1&Pr63;_>Hmip0}tsd6zx1i`(P%`vJr)gd;fDruvBF{jJE>4R7%^dK>P9 zkp3kL4xW+RNg?8IT_|@N1^U+;qZG<_JKx$=72ms^y$1DD0^vGLh)-hj2-HVA%RRSC zMiXfaX=Vq7Qt(@Vm)}R$BBMBA-uus!artkzf40Y|lRk`9-hPiCVw zr{^(;ljh&n&$VOQeYo~o%6XTA@R>&W*Mjy~0o~z9R-lDUlCeU)ir2Me#ay() zi^?3wRl8b8>Fa?+Y9K%$&DQOWe5b+@5Own^+VR#2zHej^8>9O*{JWGy>APN;&m|)O zFy`3=o3_;~62KV|a0d8A4hdK_yL+y1qM^n)`3e4*zio2U!NUn8ggSr7F;Kgv;Ra`m zpybB`%yD5go$Z?@hsi4?vjl^z!!RBzM*5_KEv_`AsuZB5*bL_Pp=63mmhg41yzJ25 z0Z+qC;wDoy;f$CayYHI+ZlNfcjy{Tl zR#8xuY*7&$;{sL+TF@qW1al?cDHFS=C86**Y9UupCh<_*4 z!{B%3Srd0YzG(H8t7BY%!xRf$?h>Z9!e7j7w8HdeP0_y}GA{WK4Rc0^a8(IsC4V*z zH`;lwSw_d1ch{+}g&0O&vPCpcxd~@|im94K%!r;tKXi#AJ|EaS z)?fz=Nhks4&z}k{eICbazFaT>8g)p5HN|G*o{V=;IB!|W>|I^IEmd0CW;P}pE^g;m zyEzA-&P2(2lX0oYR9wXdrUx>^Oo5-~J$obL-2shZBbg30t*RHc>_!-X3x0Wr2l?lU zOEE&&Rwm2H>IlKYG)kq|o*jv!1q7Nc7#!$9v@w<~Dud3_`PFhWTkJxWeDNmPjj*BkIP` ziq!|hyIL;`<`sVLU<^}(z;>ADem^iT8pP3Yy zx4e!o>KgEcYpy30nC!K8?=;Phx3l9XVtaHL5ZC|WdZ{aPgCAOwZO()`9m0y~PTpBCJ zL#JNn+rP_i3&=Mtl5btRDl`bUFg5l<1l1_JF>YpUyL9e0{x3(WX|d6UJ^006y~WZ*z8)nX zUb;)W?5x*(Ig98J&o|iMy*a3o@~jh2A^0_E2vQXp>64uK6FTwi&(n5b(I!Z)z69mgNAKjNUvS#f%HSE=0UMr4Hv*U3zm-SlMd z<&~*CZ$@kt)Q3D1Dr4&G2h6O~4in4aatmRA%37_x?_G((t_AXNOaFAf{OviT zNPf}VP0U2koHewPiad!!qwHi`RG^wvZC1Oiic{A6(^p2zYs~2NMxV3 zFs{yhc4Y0Me((x29kx4g_*vb%9!PvIKWXEO2+AC5Y3dq0tZB0@tVY(XPxqPi>q?Po z23CHiAVe^|W{c*i167R~@IFo#wWsO+mf4wOd#B4x;{7Dkq!+6+HG`He4vrI7GjL(e zJCJU-j}rq@Frtl(IF*5nFS~BhQSA@5S%jQDUNxS<{x0d1EtQ<6)o9L z=FdqIE=`>n(B?j?Tz%*EO~)S;DbH_c!nWPEu4iSuM^bExF6L_Q=Yl%bZwWG9!KXES z7&Rg>!<%GSIdhAdFa5GKd069Denb>P8niqYT5a<&E8ieF ztMeu)Kx$;ftv>g&+4!EM!$n&ip&jE1@%0w}UpD4TS%bBTLDo8@e&n5r-xZBz%5j@J zody2F?649?k5*Tf-u17|g9aL^iyCN(6zAW<^5ZI1nzBM2jI#%ipath8p??Y_$SU8I zH>pHAcO*#(d*6#XqD-qe-@!rfZ87wXxHmHJ zw1EEOmu*Ltd<4|EY6!65{3&Qw%AQFawKE`l{AWEt9_dmdP}UazJ{T1*xiH9f_iorE zDT3u)8W@nT9?=7)|1eK)c0b~H%t&(DJ3&7-i+s1|uAgUU(6oQzb@v_?RUw01fwR!T zFt2M&=gVlUcXs(quv&s#vd(r<+-BWzt*RIu@{tO3fFLOhggAZikW667(D@QMfWwMF zp9F@kS9mP$x^n0-)@V2+tIZZ-wZ;u&%Cba5N3p=CGO5VbrUG5a2*>!Ng`sSSA!J}L zomEHAt3C}07ID*Qym}G4cYZFoL$~GkSZy`=mG}9Z!A2N+?+2f0Sx?s5eydCNNdiYn z5FT(~ky?Jyv^368>5BwFx=hG%oDPa@|KD`nxhVuLH{?iwZ-YtRcE)8Ni8`phPP zxtH}<5M%*MMLnd!!LHSfYb9i68|@*^>L4_O{wfMS;(JcR(2lgZf#yAP&p`m?Et8odhU#peE8(mHPW!7xt)iUn{4 zLX(f4>J2=<+)OE`LO+pdA5v~~p-X-g1>8w}nPkgq)Bfgt(I%U4wj+Tg+2@_2Rqdb^ zx$;!}dV=ucCSWE3q^h9Zi>0i;tjn*ERruJ)#!R76-IM^HBzAbm?J%;fRM748g(?Jw zOZ@XNaHR60chb@#&tz{(f8&zF$7M}+CpSmVvw@5NZ%Fc ztAD}A{nE3dRg-ZNZ+uAuRJ8>o0mGn>c1ByReX?C_dYsSx32VtQg~^78!8MA#BdRe8 zpj8t-EhLqr2=;{jO332fPX~e(85QEBM@0@OzFv_?^WB@Zd1T5??D z8R_>uURugv*@vf8)3hce)!J*=h~)&En>bJchexFyHV#rn0PE+?1Hpdk+hJi!v9yF) zs4-j>zy?}0qepSTdrZ`fAfoS5MTl(AEOwoz65quuNV>mz|Gggw`a;iby9YAngC^me z#?esnhpJ&QVE?#f&}$w}1l8UL*r)A-PH|4~MCFu;GOr$NIjo$TzUoOrao4@D zsuBOJrruQH3<@(Mx?-aM7}`nepCd==iSW5R`dmZW%ntRz@Cr%H=G(qGlImevhsgQv z_x?X}7jLvY95d)l#yO%1Q2)fRRDC(r3WNYee={expr}TP-&LqBC7Apk8uz!fuDE!% zV>@P`myg1|Sy2O`KrUKswgFCQm8#hEb5c}zRfqZy5dVZfhOfriq(Kr@k9z99H8@^h z@X|cc+mNA%u;zSuq=>$T=G-Z%s3AZ8x8)I14X>eU3*Y$_0)KiY%R}6h(%Z>*eW8wK z;uQrW-(PPb$Zs*q3}C0>J0r4I?RAZw-} zyrhFkiI?`IoLf@clgI=#f3v@R7Igx3Z0S$|89vs0+hWKbfba!(g&^oe0_NT>U&URs zUF7up3~FGoR3S`~K#$wvk15>gw^4xkC{bnti5MrZ+WjCkA3qTzf{a0+j%5*((^iI{ z*?>Eqj`Qy(n|{)e+VM*zMuy`IU-hSw=x}>-5aoeY`vy7qkK_pp@U~Sh+uYYEGe+Q< z#Gv%7DG3*%h;hbz1x_)uvyGdZG2&h;^VWe=Y2h^dl}409@m^x(gX=o7Hh*8b0ry24HrA%(sXp2!x8=TeMd2-l0!sX$hi~n)av;C@ zA%_jUC_=~S<5R2oaTKpb?nFkTuv7xXqxZdKuzq5X2|NWTWUYIC>)j%D?XokRLzLeg z(-3ysR(9%4eaLCy?qMoc^3r_Eet7KDI|NM+7f)0QK4`s&97XIDrJ! zDSgXdqj=)04m5ZCu9wnwJLqb@MAf(%HdP77)aA#hb+Da(xsHL>Yf+N$wZJ8J!aqyIiA@7G$MpdMqLp8J!`GnI0xXvV-@h@mx~?oZH} zQez23nC(Zx5SOH-tMgi1q(R}OtH?g)dw0G{A6r$?z{4MSZEtSHCIZ-SLqyrOlM zg3qssv#$@OM7(*e--HYu-QH(ps05@839JSit}}Xi;O5In*ci;SHjMvWUey}|4mm82 z=?dFE`(-UiL1)@qXG*pae)G3WwiGQ0w?B^&sJ#`w_Qj`ce)N)FpD|Q-maw&E9TU36 zz!i*o)>1iGt464B8rf&Q@A=?o{Cu@KC_RI=tBQZz0yREvru>=w$v*Jw-jo#Fov2&Z z0V86FJTaT4utkoqARCvTsa)rz+&m&ECz0nwnv3S!TqnX-zomlivSx$T;4NL78+srH zzH!!V{QhiU1YRaI8n;n>!uay>2NMo_J)DwXu7gt1McGEb&9T>Pc1@ODAOnZxH_+kB z)dXO{0`oHdhp4#xA|JQ(TpysXBaU|SxlYy|1GllDI%W(|vdnf+aNaCpxIZ-@;|8(J zx@30RBzEPEZI-2hum+kG%u@48`5f!HJ&Z74dPo+(RYc%Uqw-+8OxuzhOxjZ)#Gik*T>xF`zRW>RV9}eN{w2dL>N^L z_AV9;Mtsl)s*E=!=$t3ALgXY*2x#qJ;#b!`g15kUn}8BfYckyOC!8?;qDn`Cli_@MnPNU#i`QX88X!kUa{i3hCkt*|+kl^qk%b14 z<*(QSWq0`RViB?ZRF_zaw$rq<3Vr&sUTu)yO$%cC1TM2EvVn~27y_J3qtBI058D07 z3h=K>+YzYgyvaFiw(SIt;C?H4rf+jVPrLmGt`GSNSUtnw4<}lztKW$<5Arq|cHKJP zQjcmkP|!Za5-?M-T+me!0oCTj7tYN`BtvIVxr}^#yKbfaa0s9^FDrBSlj3F}P-k4+ z_}lvt+uVqlh4g-W0s3r)uT|cI0V8zKa?Y_t$Lk%oj6m5HJMTq5sgD5`xiP)&zy2(Y zzxR>%otEQ%XnA@U&rQT|VqrH$g*!LQI9^i_+@A|hM6NPL8V-yo%fS)+ zQ#^7PPk8%haXxh+D0#--_Dh{5j6fJ2I94I3i_Lboqe8Q?}$b_2>KmY zIP%6!pjb5?ACbNb`-(9rFYsrBR=W9JLx~E+oFDRj|Vo zkK5>7#EFRG1OcHTpkj*V%yoKobPtNyckQSg6*uwJZaUFtOz7J`2 zLCx~7G;%#B)#$!Zc3ZQTd*wSqI{Jt z&0m-|i~w9^(tt_l?3&$o2bYmt?d#P1wn8&g&azLt>@&dcJn8d%o7=6p7YzIPJ5h?! zdrRI60#~-38iv_$CLU#slD#<6UXK0kD=TTQ^q^(+3ZT^M9C4zNx0gMYG6P5oUjqWk z3M!A(ZovTW`jFGNB2+CxD8x4u4O+77n$DH2d+$&U1u_eYi=^%rBb%0kB5^O~aB5{K zFvIwezTq5k>~4!!)E`*Y-CDj^tcyf_xS-l;zS)m|1#mbDCAIiquscCk=rxf+8uuL) zr1}SH4Jp%OD8r>rqE3$$nm-`F%wOp&&iQ>5s4FxKUfhs_@D*c1w^T`Y$el6K7Tw1g z=$7}7Ms2%=v%GWVOT@wC0QM_Knv7b9#D*6X(l>+6Y1S#JqPdNdB#GZBO_6qBP-Ix7 z(R7I!&ZlojYyeOr37;yZs1Y13Z>D*YD@u{qhhXERGE)5Nyj>rzRAV`1hZ_F(Rci5@ z98g7RvF@z&r;d-?hE&1O@npLL#<1C zVD0h~Y^qr~5y$!E$`czPu?4=>&P~8$tN;m-gmx$5)aF6$AX>*^HLHTAD0sh^z^ag) z$dDTx(*2N8pd>5Q;q`|U-acD7p52FSt`94aTZvL%*I=h(N#?rAq{MSq z2yy1k{_(hoUv^7&#xMPfm`(e%Rf^@71TANm%$I)Tm1i0-N}}dl8r5@NK9f%RIyM(I zx*@%1O@v_eWYjm}6hlx=rZ#gG7f`@^ZD=mLt_FGjb_pUCr7u&c(JaXF)!`!bIW+my zu1OYoT||TdX~4doI`*AuFnLkTEaKQK4UPe@`>m9qZ{!mPj={U(M8>e9EBUZ@!4T5B zXH-ccAangH?{y-EONa7t2HK-y#gUyfjdKY>azVh3TSxKYsg7()S_a#lD|s#5Ymy(J z-2sDZ#QcGwP^G(O(0BFtv_`SEg~M76mZJF{n5)aIo^4Bj#;-1?Qk3Qy@3ov}#*^qJ zlJEF&O5&@Xbp8r~+a!Nce8AHvMgF$u&@7*qIwBd#Z-K1A#Ly{1C{zB1_m{)L`ydXq z49B+|Bs=3~GJ)43Qp+?iY2iOjmg1@CVq+I@?D5qP><>5u^z2h*6-HhB1y_F2l{p%7 z_!oW`;}(A;D$6xmElSj06pW3nz?P*%CI1PZeRFj9nvuZkWXlaMO|!||OVl;v`(+Ua zG3FBkf8V@#mz`s!4wjqm#NIaqU`JhThrMC9WJ>W8Y7Uk}7Q_R)!rbRML5~2Ilh21R zjsTuPh^_@lHdE{%Nt(-EWD$-n2z{{ENbJulh-^yvdE0u1F>W-{Cl@(O9_Njq{G2#f;&ajsNYj!M zN;{<0)Lr`li}0eWVu_*~9j+svzkfn#I;yK^bk9*bbR-ir%?M0>{ZmR2{%n+|A1YbQ z=;F>$uN<^dWLLUrIYQ%5VFgPCKO|&erw6`I@{p3c!=dg3j3R(<5P=NJEV4BDVa8Bh zOyS#_i?;$biYv$=Pj#;}!T_H104t+|eoxIH`^XMq#pKb@Dy+~$MXFbo(~dd&$U=lj z0!i$=0?^PNi?8&rzni53p-#eS_uaikXV|p1m8;@9IGmqX1Z5<0O3i}}c0e6rDMenx zhWa~xBjfoa6L-r4uM2cJZ^z%QBok~1uMT~7l-E=`eFQe zKlduTRI~IGmG^KPJM!?iA2PO2Zn+_43i=FD1QQ($3#7Cs|HZc0KSb$9XN2WXq)tA8 zc-H}a?*ZAj;YKVYF76gKrC+lfkc+LAm;tjS-)OSO?l>+y(Tqh$MSsc&WcE9rp8otD z2%sih9l{V53)-5)RYeG7HN3;(X@++XlL3H7m?7wSlx zhMkU%P2qV~@V_&~;&46a#4Em1h7;SFKI>GqC7ultCFK4d_G2b|LaJ=+nQhM^uZ;G} z6lHLV+zJgc=NGos#?nOIcbqX|JwfOm;F;x3gYEOOnYvJ zrkX3RSAs-NmCYmmYOTyfd^bdEhjkh!@ag#weV;vx9t&QKt1<9oMbU{i2Gnpu(0 zzO)sopkEHg7xtf)gkLmou-g0k`|R4Ib6@@?&+Nm1o`-UdTZYUTllpP zYD$nsSxJkg!EargMuORcj8g{9e;$1&M_6g0B)KW!qc!;}=KLM!=Igm$wVEn{3M0sP zT>(#nqEHSH9R%pO)R@gWn)>}@zW_KI!F5R8oc8)dElyG^+x8vcw(5-_20q@Ow43{= zR=}`Dy;eFH&g4kU0Rlb(vQuKr@&RjKk6c;~TcjSWpIpZZDo|g_qW+(PsxNpbyN2+( zWl>*xV{*hd4IcZEFx;`izognApx%gp!b?sYNQHS#obK@67h6=eE>n#l6la#0m?g_; z*bjb3S^INg0%ci#LXl-gK4o5dmD79MkRuQ8niEL;sOjiO;pV{_8rd#DGvwF(*g<#` z=fNgxe}ayiFi1~>aQHhwColvsGGiFN`cDxqHHcV;Q{`*I>_3M?$o9N=#Jf_{Z*hzZ zkmf?zeL*m2N-u~|l8*J9u8m%eON8UHPGwsYiv9l3S%XGgpzScoyCfSQ;4a$moKyP6 z>`bu{_kjwpRV*Vxf#EpQmkYAoMXY^+FPL~~eS|~F^LO9XwE}jDD=1d|VR^=4YrONQ z_m7~~XRXs6^1!3hW;$nwXg%ctkq~F4m(opzcbW3en&A2&S7uP<4hfD6Gb0G=IEBLm zXTyv#fmiyx>RhpXWS;8miUv--|>vHf}k&0MZVb{S}E?MOD(OwtzI&71`st-ts|LPDzdo6|QVJisIK_-^gx zx0~nn?~WMfNW@|Oy)OxkLU%U^J!4tDO&R>|JWTsTI;=GP?Yo6a4q{dh;{h^%);2eO z9I1v&JJwskhFF!*Iwr1{bGF-V(CO`34&z^tz=BXRbo=`W2_Ztjr0n;Y+j; zd2B?T^`011q0)B|;X{Fw?b1D^#E%g9W72})s3oWx)6L-Z1%2)rE=sw2D5A^0 zJL=-1`i>Ok!PJVy)eBlhogPTy^x8!Xwq!Xt#_$w$)}yjWm-Wf*w$QkJLC6H6PytpaBo)FPmJ8aWkRal?me~49`BD91_I*+`W79TSurszS=9Fs%!$PG@wO3(3w8cz^=P1+qFtHEGDg z_sd@iNHD)vOqdb@D<5#jA(<({l{FoJK7ki4C*GfAr9+vhfOYhdlVur7RYvb9Ixiat z!yN3p0lMm*8*+ZJcbPm79HzRy{nsn4I}%-?(ZH$_m%EotWIPGW?TBz}MOv#z zh>nKugTvWX?Ma%A#WTD=&u9({zoJ~@0Lw9>}L ztdZkeVNM{PSfLdG+Xd~|pXToHI5!9FR_nA8;Tmt9ZmJHFA1ic1H9EILr|u74vb(p@ zwyrZ^-T}k$nS!KO%-XKQUHOMLaZZH^4)6|2q;x4ufxRMU6Q8Vwe7t&AGx^r$jiOX4 z(^+a%zX^_83W2P@1si=uJ`EBcUmt{&UZC77vfF&NCEKb_#FKBpD-UZzRjES(g@iB`#e!hf+ zveSfbkcA34WQ{TGQr54iRT_hrD(=NsmGT1hG8iHX?L$1}`_dYfaw!pAEFa_tR7KrpGdsdRYamrX zy~%g26EOW`$8((_T}z!k#NO`)kOKcC$hqw}cT6BE4w_GewF z6+bYmUgc3}gh<%PlurxTfh*qU-kR(O&l6YB%N2kEgBv~@$d<)=o)BjP0Vzx0!+wt`f+ou6<|p0t)#ruB2Vf;Ija++i6>BFeOLe zDZDG*5HsO=N;mSwP<&0{7WlfS(<-7djR?;ry~JssYk4o;t8oo01ZDvKNW zpC?G@V!D%%j4wP^aEYTco+#3UUjHEJ9(RA?KS;SZ)ib%WNHtg@{yAgRiquThR;)6Kxd5&qUS_Xt{RHsI06NsD4jq@zY3xROCa zl4Ypui9yL44>O_M6aTGc|J%dr$9FaMRfS$FW(NMpnZCiC?yFOdIW-5->oCfylMV0Q zB?Cs5#N_n=H^OjruEJ<1O@WOB=dZ&)O52w%M})Vvy$SOH20c%0++GL%fNbu;DYQh~ z_=AWvs$a8s8YS8ROa+M`m8kygD*Oklc98hv=XyVjkJLE|-nRe54LcN@EYu+^TvNV! zgr~ITLVGKp8?%Hc9a*2zS6J=3uO?d#O);#GLOAnpvoN8JrHg|*wks~(a_#5u9;xeG zzKcv5cri%fdN7phOyYRq2G=yrKT&k+w03Mg+9(@N&)CGi!aQx?pF}@X{Pk6z81Teb znKW_JS0pB5q@E}Eo=VpL;bXb5T1~9ln?L&hU^8sVvb<2JGv~!jPJjI5IE;CRR3(C78D>+1W`|4B z2+`LmGm%0hjkdom>w~}lO(v;)a~p)B{2j;3+Hh+eWWlxaP{2k;okk=sDZ6dtJm)6t zyR$VBb+P;Nc$R`OiW3F;4+hLvU5``+5QGta&?qcFeVDfPjBTcy+av5421AW)RNF>= zvuCxjH#5s+(^gqYgb^j4*dWvqVpm#3vCUF9=jK?jf?l{tfU69DJ5~}11SCL9ud7+s zkosIU=7z@2^n9*rqTQY;1k)FP)lWZC;KSGf7MhuP;2~H*!0szDg z|9lsqd6d}2NUZz3z3;$(H-Jk_WC~Zmb3d-RIWq8E@1-E*5H1{Z_%!uLt_Y`2c)WSj z<%W93Mk;t?Stw?S`$(!&7{pygbU*(4UigZjI*!vpv-xj?T!FM8JsSh)IKs(^Si-w? zr0XN@fWhSSOL*z1wP{_<4>`KWdU+j4d0wcu^r|wDqUZ&QAdMIQ$1gUEs2_!rG^w5T zt==6C_SF{6iDpxtKvsW|`la9IM#UVZYqNl-D%a&)K6is$MNpTgXLXjaM++wq$8vI9 zjv3&%G;2`hvh!Zb>3wpYwde7F@xpE*jDV#2A!jPHijQjVY9(&1Vn#%IU380G2XxOE ztX?h_X!yZ5c_MkYldtBP1emRW=S4{UQ*RwU|JbmKlF%MSTO%$KBy#R9`D*D`_V1i62bq|^NOJN%I@3z zNpQ#i>*c)v*?hl09uY)UtfE!Y*rP^Kdjz#gQCfS`nz3rfh_tj;RjX?6Rn#UGBj~iL ztyWrF?7e-{_VfN1zCYfN`*GdpT<5wT*Wc2)&^#IUMcx%zyS>bxRNcJy1X_564E@aAHO1|n;1&#F&p z8AYsM(y-ALL=_*EOC|i6eOmqDNbT`*{HZ})v`$^=SDg8*Y(rdBCVs!D_)^r%@YbX| z?`A)K>$QD}Ea@(e_u0@nPTH;>-D*CprKSJ&p2NO;EiUotULYfL{ye@R?P zOxrP=UbTAu*yV`oT{4aLE_PlubM!e?ppVLnkznT8;=RGfX^)|K$9B79wJT&%LWAbD<9au^x|%9a4O zA62IPaWr)A_wFEGQeo=Z&XwZ)YL4)R@7NVV{zO=&&kiQ-NTC5 z6>F7uOu^CQ402$q*fvuI$@}CtzWJ@CbquV3a&duoayhet@6=KVdGU7bM|!s(n-%$Z zJ-j@zBym9_T6nEnUHz}zya}!=YM)x_=5`a1>Q#*84L_^Y%%R_YrkEb4`&wf-4XyOv zDvgp4{Vbsz!iCGrvMlND<(V0#imA zk64%X{*LI&D?GF|pXwj$eoR?nml~dj%t>aY+PE1*-wYhpDqct*-61iQ;F%iey#t^= zn(&CO00FoY&A<2*eO6_MoEA5~`b?l;3a~Drrl>;A#5gh3shhQ>m8*-D?&MbDpU-(3OjKaG{{C#No1-wW zf<-R_!zw(3VvJMR6deK84DD2s24IG*v)_g|zdQd>$*@B!o$Cf_esK@1FdDo{rUN+l z5;AD>PwaHTFa67X_CP{Jv_bWErPDg@R-91Dx`1P}J&5wGz zCUptWe=J8476Hi5extNhPTEYamPOgTAqlLUFN+#5=JmOy(^SyF$lf^Ez{8XDgLTqM zcBEkUp|tL}=xBn&zL_{U9vf{&LPYqC{QsPFVjG1MZ8EN~1&r8}PK;vo3_NHjSfr`YgiHD34T< z9%xNY!$$U)>^2xFz`Z?}B!uHg8YhW$K4N}9#UkWo{EMh#D;@?{IpS+;dR^aA(RGk# zFxs(eaphICFu?>kMwhH}TcfY{I(A^v1#?v(_PhRQxbFjLomXiDe!jKEIk#(59)+%X z`L1%P(jKY?7A&WK;^|_0b=Y@IY!E6t)AW9H!|SmbZZ&~#BO}GQmWv%v_lb&x?GcW6 zb|k$kcr6tZv}C6zskmd;ipmA&gi;E->N?i1%UV7GkUS^&JVABwY%D)?5rE$i-hU%vz0R;%fIW3*1>xjb zp|VR<*|(V-&-M%kncIfvG!OYBpv%b>^<%(TXJ2N6sYCa?WDnNTEj@Y*LM__AyXL>gopwhvt>U8ZrpO?sDLu=_jU`*ngXytG04~160?s8gq-j$S+ z35&WtoQ<*i3DpYyu|Xk;ssY1UZt28zqElxv6am4Q?4*P@6(L3bLpSK3-iAfGrxnt} z9uSMuYOduk);q5zWD8poE1hzQ6;T+1kq9qjlX_@sxn>SwxH)GqJQeTjKP4Ws#_JuvqYNpB>^@{Y^Qo#A;{R3S zg6w*I1HSa3mmfs|j_qe31Hv8|4hUfJK@)~ty2dAsea!F#>)6{Y7!g1}p5;yidDhv2 zorsKl(Z|7$byJJbqjgfpI(2bKysI)-!0OME1xl6P^`pzjoTQfa!jJsryc(TMLiu;e zRgqjuEx9ih>uhOy|Df6EC z@od`YfZ}>^D$FT;72ing6IPAlSl)*p&knsSvFJg7E(5uQm1^gVyn1y=N8?XU+M_iE z{6wV%DS+JI`>FtM5=}{9q5tl?Lb7&Pow2L4iw4Rxx&UMl-s?O=^Hy&z>C@Hxk2k@x zr1Wb#WKX}4Q)(y_X`Gp%mQAJX$bgZ%M|m2$k>O$F(gv46RMKIBFfK!3&J{@L{tBc= zbA`6T!C4vTE_0QTU<0hsREKz6lZ)pjG&cnEKax9j>R^Ww=`z!}$xI^Qb> z*;teY#joe+k&ouDv)Ij3=>4p_TGb{pmx_%odgMjls_t~_4R)3Gr{3I08E908n&mJy z)rNz8I_$n?e0qz~?+fu{FJ4;@`UY0rZuda`f=W5Sa%AffEr0^FmR14=3|bUO`aiGr zuNP}d)Y-0tl*@$EzN`xUfzMv%8Ir;HT!&r|MiM>1$I^a%M)J4mI&sb9dc2K$Vtgd>32X&C_yyaq2PB z{&cXi{WX3~Y3~=I=xve@`pbm5ZJ+fNR9r~~z*E~mW7?A{>t)>f6tpkzHgk^U%Ua%T zN@e^XCx#aN2Sjouab1N~t2i01&{8_Oy|TT?KGF7JRG94OPG{QZdAL1n!N@v!OyC+6 z`R&q5(Q8R9UVykR`MLJPfe7_zYVUu@tK?YXr;0dzqm2BNVRE$DjUAk6Rc$xBn3dx` z_c^TUTNpzMynl6dkNlQ(mM?S0(~_tdo#!4F4KK>M$ybW&5y3L(Z*gIKz#@EiRF8p$ z5u-4$KiXv}nq`PGv_cRCdBXa>@Tu>W?*lTMYK6`u?jyEX?hmjm>bDrPlfpjMHkUi^ zDx>%Q0ajRKfw`1Tm%=V_h~#nrZZp^D7IJre+U?wqh@Z_CVaR;^eSt7l>;qYK{{)Hy z6aeOiVBax6Jdiz=nPsmVP*=tx|I*i5?ZC+q#1s4^Uo4I}k-Lu}tOJmb#i;@pc9#+v zsbCr|j9WftAJaP*TGwNFu)w~e19u8(jOE3<(R&FGZl~^~*o6FLHvO5IDL9=be-YQc z8nAA)g#cua#|ow{bRC0(C0Ba`a;xBS!)JMSA-V_tPA$GoqYq~G+1gOdt2cK1E{-yT-?zGYZi8{&>v|hT>1gZ0~P~40V6&Wywr1gU{&dQ38 z9BYu;IU*aaJuVpOb_xphihs~}P$Ws%v@~8aYX{x_FgtE<^oNQKkDXK= zRkc8cNMXC@(mG9+X_DS$HCx-!_uCWC&0L!sO7I4f40hsQc+e#`U0*^q(hMm`gdsjl z_49Wt_=AMFGn{MHK5+a)Dt!VE$)JsG&sN&fC1+-kwO~44dH~DT{D(Pf0myi*|ID?d>UvTp)2qD{V!rC^%ihX9e=^O{Tb`l(vk^hpj#N~ z_H~`?ZfdfF&S~jgH^xF-u1yMu@BVOB)BE3& zBETk$XyXHy4XfI~ozTwPx}@54mAnAym7U`P#u}o3y@>3{wfAT`=^ooO9ol^wFQ<#U zZ~Jz;?9Lq5{$rkPgH43c##pE?(2d4YE91UdVIUN644_RB793H>yU#yh=V?xuchoxt z7lZJvN+;Q69*4R(4xHQw>HjECLySNpJ-ne3LOP2(M(%;wZ~YVpVc<0rOOG?68!$a2 zuKZT1Z#uaW-bHClZm9SyN#ANvWMqZ5^gm>x(F>(#?3)5}7%q9A3Ezg=JAV2{S7EoH zfXv{nh?IB_RGUkyuSnKqxdA!zmkPs|`2pdUci4WSWXFhQjr(G6wX&=KLpV~?qRBPz zhF1s0xBCnQq#jdVyBO%aRxvkWeYu+Hv@E_=f~dD?8`$4aT$>Mxp!--S&)*ICdo;pW z(d63rVaw^5K~}@bHfFFA2$0k1PX8-D(qp+ig=l;wgiJSa@Cg9ls2s;8h5IEjjKuJL zxzmmO2Y%FnqScl0hBb#W7?-T?#a0vmBy~?2&SBJ2oX#)bx2)pVay`xK)*ooyH7VRM zqG6A3a)_4$f%t2^zn#|UAxL+ThvehfSz%z1Dq6J}93RWb`9r{`T;Vo@ZO|EQ00ee~ z%ziLYZ-9AXI~sqbxH;oB~$*f?40K6~LHkI*9+ zCEZ~nZN*)9Y^+Smos|8j@#%9 z6f0oeC|4iqt?t-vbRah=@il9?pOUt z>;g-bp5ooRp|E)?ttJR*(DhsjvlqKpuiZCP<9)0GAOX?7Z)tr>z3JT9hcI68-2AUb48vO-yXstZm z5+FF1kv2p#!R325dNO->CR?SrWXD9nlKnfEKWw63VqgaI_?$J&YI(`|1#uvZHbrUf z7vp&Q!lCkwpA1Q_$n-q;2y;evUl+QSq8cw1F#hOiT}|5znj9}II5EoKfKIJi9G=Wu zfMGgC)+dGFIf1VIG zMs<~Mrz!;gv0G^aqG!XSD(vdV*W%$ra`U=y3&M3Q^JHOgRc^xlK3x1g?#10Fe)@}4 zdKixk&t5Vrhp(b^AX+5=S`!4`M2X^T(|YCBZ%jTMNl{$95(=3%DBBezJvHv&>9$au z7&5)uz6&kISpVew6Cf^)FaTVmjAp$2Ti!XI4aajM{=ut!DgjF-E6>vHuZV)4&sp+k zL-$T9bc6!kDiscV3#0}h#HCb%VS%8-%XJs6P%YH@tI4I(NnS^SfT@6$XUz6jLvbmI zmoknK>kHf)yFn8?4mdQ^-^hSZg_XjL>**mAAwqfQ(~Usm(0c=k7yfC?9}ICex6%oLu+PUsg(wntq_on@EM2IN zvhp!OlO5A7xmAQ_$BQvTcZXgXW_OF(zgt|%W`r1*56kC)vuW?NQ3a#oF^`0ni-*s( z3$E)5d6-xQ9-i&M>y2kbNr^##oZ!s?P5E`w`@YWY6J_083zQdBPU;5gCr%*ZY4_~UP+rqK zzGLBi;>5+)qwka0M@0@NPAHG+YO0(1+bm_#q=@MR6wI@Ch5c5@PLs)SYQbQp&xn0q z%%B4GN8Gwi?S97!asGwWoriaxzB;jR_vwk}pQu2LeE3s(C{D>30A@30D?KoePvBNKpJD)!Z;OYaoRkX;S9lo+>3DRfH}(I$8w(PJ4;EJ-Y6lr% zVrs~xj2HX2QC%Eh%&mtAyfo%XI6il8=F#D`@g`l<(5?2Ig3(yO+`EM%3-n*9&aNCT z*qSX>_d@r_)%H3o=52Q7!*+)5_iXL#_N(cUVP^I*%{%0BOp zCNEFt31`^u(BUW79;Xhu^wQ>o=EKTe!@Yy}!@ctN*%iy=qN4IBp}0w86DHNA_W=|Y zlg4*Ff3%hB#O#s>8mEZQt>eL#p%T3W zigQt@TR?r<_$kwe8N#e|jAx!>wn^i~i$UuEAXmoSMuYc0aZj8?WnBMjK#{V*ryp9x zzJu)>M5;KAL-xy<_mR!NOhi6&z_hnbhfl)qZbo^IkH2q%6n{5-8MrAff3B_=vl7yi3xN3jowl<3 z86Tggir<4pXoBs{3H7?1*V8_p5dTaPjjyd)SJKkAY*Ud41>*bzn8UsZ7_4&BI70^`a7W&)Ztl#dnG2)X0>X z2ZKw(6M}hk02PXx7UAIwO1}^$!w*s895NS-JWmv7NrC&q=0A5id+``29;-B5LOv3GO!D`Vgt#u?%~79Z(Cg0DFY`ael`zlqU* z{&&C%CHTJPvC+Al*&{$mP0*Un!`KalII6Q{(|IcA~6R=LJIFxxS@isRzD;d52Tj}gecWKpK zQH;T!EJ$oIGEm=SXuQbhB|yr)Dc>>X+5b}_I3k(zi0QP)1llpH@Kk{+PS8bqlVER~%qC`XlvCBKnTv9FQ;2|Ax&X z3>>qdQE*M;7__OzuU+oKOPT?g^(*~va*sWZ6KTKkUTbiY1HAjnrQ_Z)A|P)Z();*{ zk758JUvD^y9FYx&%w1^%mxPU*m{e7Vbm=$8{^1n-KbUuJDky9{5#JNKiNrrV{BgKg zeE3TvOp#oia{%3RqOe-A++-+0a;u85`7pov`sm7jw{6LYcIaMW8%K=AR_Um3)y+|z zc*eRszV)4b6(_Yqbnm^@ z4)r|AK5CclP&36{jDwy8-=C}lK0+n_oj%;NqBHmS1=a+fRMQ~f8?NQMKH(p+v7VQ6 zWRO)JTuAFb6Xg5c$Ggrk&vmd0JQfJ-gl>%I;3fV+$~pe?Z>gf{&8y@WI*@F_BVJk` zHi=XZ{Hmuof<4CZQ)XcyToCftWzff0sa++CIM8?`0QY`l{ z4gbX%!He1fc~t*{B`#_jKE7LlH$|B>sp8xNg>Qd}^#9PMjx#&!zSlJP-_QBRHvT>N zBHHiMMOTz-jdM($qwn*7Jro6@C8@t-sB?~b!E>7M{ytSaibVL$2PvLlCtEn};(QbS z8nl}#{;WXL{iEdXi{4If#0_6JE@aj}AinrQGcw1=+IJSqn#&^tdd+~Wu?~?@?|SjN zCk=6W^MM-b#y@zxXKf-!o3 zgLUp!B~@g-W!2+nRHZ%-hA$b#^D#Dy1P?eu;BtYPF376!e<3h;mN|)2WI=l)d z_9)}TK?;i3vP#GQ336|~K^i`7Vdm=uJ{jFh@j2A_nx;#{lnJD#bAAX}kg`A=Qw8#J zN&hA5G5h2iV4TN2;nRZSukce3&Mv?YUSA?0WRNdw``YsKGw~PxMOYruRfbuhvB7ZC zoc@wzxM!YfeR#>}sZ*V*NU-#OxlQ(IE@%Rr?u<23#nRqzxvv>$Z2R>Y&g_L}uM{qG zC4GhM|A$nD`5%~8d}6DOcZu{PXK}ib{s6&wjSON1rMAg4jV@mX8=`n+YzW(J!w|&E zpXtn(1Clfcg%xODC|GQ^iJ&Fm?gFUso0v?f8YFnY6AtJCO;DJdLhCXFWl`+mOEpVU zc8{97cE@!?z@(xVjx-?-T8x-cXFv8+%pf%2HI$bqr0&Kq-(9aBgExlR9do8EVCW5S z8J8L5_x&$@MpM;nnfdg?Bk~c^TiF2dG@*HW6mw;veR+!NN&!L^b1Np+FoRyH9uS`R&z%&fFf&copJqwC`$+K zm4#1g^lX;-%3mwrY8}Vr^pW98Ep@s-(So?oyrOC_6?%?yvJZW-hR^r;%~c2rCz0>5-y4iDTAv+x_s8)sSIE%7MaUjQeW&W` zEa(?(;X`e1{O**$(P_&32n`0hw?N!3u(geU0m-}H-J1u9^KYm9=3i6Z#JZ#2JeuM2 z8!6ixZbj-n+WE5Rb8{kz=g6DOee@ws==<@n%yy-*r0lbT^m7R7S@l}D2eOI{KIMU6 zYE|N~fZ1C>#Um2=qJK=}&m`Ue20HAU*G>nvchTc)y!W{m5E&$mMTvvY`?Jf}0JG(` zTSWM@%9*GILEhlE=LiWkst98q@50y;5Cij}{9H5|fOdHs?`(c7R!u+ltVc6GPMTRVbs_3j_uu0_hG1`r7^Q4rn~lMOOnLt zKw!C`#{5-8p-OF@;*;pL;4!y2yU8>OZee35C@Rp)D(_Ck>&m+jw+9zB-eP zq84NpA8n$Mw~5Qx_iC`Xj2fDfAjL61^f8eE0_KAK!~+pcs*xcIiwa4fT~C%(cS5yt zb^Ha_jekT&lkH*r@`A{=9bt*vBTU4nDD>?*^^&46tT2y64!DVXV{Gh(;)KCmI>nJg z5nXKQJYcNfRaJyxU5`;)ygwp#M>j{_DE(iD#tum6&(O6HNyB6tskO8~}%kY^MN#R5ONWwlCN3^IfkG7_3FkDQh!aHjC@c~SD6!xgmnUKkp!eP6a#W{TmVjP z+k;8|>)`vpm8PY&$0Ix|R&r_bX^MKdz~h^Icj&4&y3JS$cDb0Y!cH%_=y5q0`rl#^ zB)I4VZ9J%_TTKkDbPC(+rcA1r7!bD|x~MZ*z7cX$i$y)7bgw}aGxMdcW<4z<`sTOn zv2RW7Dx2zIvwk?IO#`)&@_s&UQTL>+5glce&*Vc_eUrCcEqOA4)hv^OgAB?8DrSjUgZSGr4NmSaTU`43-(tu%FiIT zEA`n&b;C<|2vp_5@vmV32Fa?#;cJL>1)fN@p+&)JC zsF8VVxXdfzjlGwu-X>(2`g89Zu=Uv8{yv^r?^1W4{knbnD}QF+IwI`4&ni#p8cL6_tnFuTR=re87R`w$>s1T|U zYNl31M(L$)RuJpxeO~WpJ?ruAClWwF?$O;$CK-ZYFejJJ*g0^Y@cCRXm?w*ETvpS} z*_`jF?j8eXt#1&w{QzM@%eT*Jxku>%z>UzNVxJP8^76BeV8;z?f?Hq8Yul?bz|YF> zF1Y6o&gRbF@m9g)C$Y~kk;||`pVrE@ zJAYB)q;&t@!+8r8o{(MVdva!GVShmbLJB$?skwA~^M2E{VPA-q0%=s9P4y{ zT-~leaQ}l_R)#!=VaZZwfHR{sJF`+`P`B@Hx>?Ai1CEe1Ru}jq`nGPiamLA(cloLl z6?5LAJ6Hd}!~F(uqc&>()n;%(*y8Cr^1ESFx6620gjU@CcdT~M?!rYhLyP5B$W8U5 zn6{s9jC0GW#gEYYZ0z-5i248RQMT05-AsBF9lX_}*)srvY(!bXd+wN|SPR}q#cJ~} zcg=<6C93~M#1HePD| zPhj>!T{3Xckc%5d!m7R})9gUF`9X!!QLQ)d_0nox>Z;NtFekBTMJS5j8JU#hClOr{ zV5AZfN9;DUz0iT_AjDrCdM7E^tnGkG9j%ghdShCC8R|(_!brq;xI|(~$g3gXkm6>q z>QJ57vAhGmk*c^N89AVe!bM}#&$^ngpFK6fW>FEQ1bS!QUZ5!u$d|}){sREX-2S9@ z-`t=Uy41?N(9w!x$vC`@pd0VsqvC#Yc4Cj)mPJPWvosrP+ zXH{0C+1tD=ig9P8m?#k^sImNlA0Wjm8rBZWYHqNfx%qM}fFvVSFDaV0l`4~^1 zL-dS0wV>Bto{%cut+Jt*LabCL-Ouou=PoJ?$t2cEfyGpddDYsl+|b)m&svS_!TWzE z-Qxup%ANs*>Yn}p$Ch}%l}$zvGCh{UME<4D)r57b9J{r|o{cGtli&q+1`Wbara`6U zqGkJ=?^uVpPm{Yfzp$v(F-Fdgvgy(fXSP4xp~HHh_%k1YT592WdEPv26>L{tlr9%~ z-wnYD62SG;OK+>JzjPf$Qk%H}CMX-6Bf`wuF`H-HVVtjC718jnugV$LcJOYDKTE3R z5iOqQnDP%<^%glM7Xa{7VvBD`SZI0Gm44oYtnxvtc^Lo*s4TQvXfq!L12q{c5diJ% zgl(?o)042^_o|naM$mw5i~05c4u)(Nfy*@9%#3%em{N5)iPdLdKh3+6@nV$i78Heg z=03o8~;V!2_tU4u7Z6jaH2pp2g%VF-#6(mM_`{Rb>Q zN(vKw^U+MtrZ+{afnc(`I*4+oK^|oj8k_Ow$&wkR?M%muvI(n}%H$yO(T}Y~l?wOU z|HGY>E%AT;otCdn*$#9<+$xUEgHblUuQ*1a28l--in*c$%FXi!k`-M4H};yd_|g759LKmzlwtqJxc~adxGrcy;>>7q*{<_(W~H-M>}d!K!eXCCUd&NR#ir>M0`50UVHV{q>|DW0oZ?(I*z${0B6{DD{$qe zwp=S&5tQsSPiC!ZCTr{9;5KD*Izq7r9s;WAhAMO`>B5>dAJ=lQF)Di?H`;hvt*>k` zOuc@zmf7&DKnA&1rj+$3FU!kV&S=~~m3Noz5BMeTuC6btNs;~x%~dUR4TUC4}6RQD6t5#=5MMlHtcOf8?Uv~ z$RVpRouknn8^t37PbtJ4aDM_V@9lD|;A1ytGZo_!2V3=AZ??CK)`xh@ZEWC09MPmJ1r}oVY=r zO|el9nuAMbs;p_T>xbJ0_FgurKw-b^jWIuWYTWB%N}N0Kq5S6D8V92&<^NGXuTxTDK@2)Ql{qM*R94@DMJC~S z$&#}jQ^QPN5=XM`Cv7^|hc-&Rj^BTImZ5&%npK1FcKf3w zr7|nJhM(3s=~;eD3tnTAH8a-8dF#imi`6z^U5g6kjdj7oYysYTkC$OB3gyixirUk# zSb8(X?<9;9F#I?(J%#MOvmE+MU9?Uu}7wHEm|)q=x0h zXE2Aa(W6hTbvNU6Vxm|9SA833MXvT(n}AC8yc+zTzdl{4THA3NgyEg2Tz#6FqHNwx z)@PRfqpzeK=_{990Rlp49IK#8A$35%p8Au_>YOC%Zty^Km{2%=ujg2{w`Fm+u>RWR zy^NzzR@fR|Bp4}MPKdU0tqx~Q&%XpN`I zzXrd^&;Y5S)Ys?8lE37RwK8WMoBwMl{e}Ra5L>HDuPxOUR_z~c3;v*~t80RPKKnYh z?d6FTI>K$F$}p-|pCo zb8Id7g@#o1RF5A7K^)MTd}ipFVa8TPdNcMXr+xm)ONDq#Lna$vC7-$W+1q>Ls|WPA z%V@qRU3uA0pEeB%SeCsKMT5AajXs~RwYX^zE(zZyZFQf3EY173&-d9%?@*--Sv z>|Vtn0B$Z+`;Tau>OBLtc)D_EAAs&ctTL%#yFhWTGDx@dzp=1Aq==LiXXIkHl()=C z*;_CYL?0UzWKO|@FZ!O50YRA~55RxSTE=POMZz=@aNYZ5x1aOz6RZ|e8KvUeK+vy=}am_;723eIU z;2Hk-3p@Px1gkEaHVDGpje7l(&_O1$Qc0#UP{B{5rSeHn#6s zMKP{^t zDmq;F=I22%vK!JsrdB3m?X9oQ3WL)cBtE&~njSEPuyWa2nI=z1|IvOElFD%L zTJT2z%Qa67}2J*VXUd+ZW8ygdF=>j_<7SJ;n z0zalCEnHR2Hvb@P;)c1!E3+L893Lzxb1`{^iivj${Y7uA161Ge_G+16Q(vkq0`r(l zZgA#6)j)Co-j3*4`YyN52Cys30oOFJKZVHDir#;O>~(>MT?LYRU(vY-La6tktN>8z*Di4&D(Q zyOr0nJXjq3q(z*(^47khXkOxB$x16lqO8Ib-gZBm{+7;W?Pvel8ExKU{qnv# zICt;R4G)u>j;LW&o4TV6EB08E;33Kn*584VBUNodb$&r%*PfpCSaCY4$kPudjXNee zQFb7G>@j8lV5d$LmX(-{KSgKMctPgL5PS0j!n1nYl*!F+uJweg(uS#$n~~4@Q9;eC zOCbgN5ZcxPC1`K~{pi5OY|q*GyGm8hM&8+9;tk$LeD}q`u(ZL=!x4asyVO;Z<;^4$ zvNxY=Y?oPgwg{my8r!j?en5{p-jU^X)m-sN3I6?zN$0>SXhT11w1GU4>sjJQ1Ns_v z*FYa89+q8Nlaq<}DqFfF8dAIOCK~c{neCM~1qMD4J-=!PyFA7zzgN1jeZake?-4Hp z)3|q6f4%mN$(0*9_buBqpYw}dZyW+(;i{oFZVaD`lN;!d?+Ld3cJND&moVHdzut21 zW0#QpVT2O9rA*H=mD02f@k$ItR5+SnIHtX|*~IH#S-uxC#&B~=hby3fr;DvRkefK} zz_>;3=qyGj`(4A>p)%d-U#^M~;5fFG$J|aOLuul6z#AGHN>-^HBX-WFL()KLWrX2b zU7@=WS|szRt6DFW<4U6a+|x-}Gh+QocVdYB*@cwYFw+5rPeyJ~lox}&W`fi5Y#U|= zHeGJ;ANBr^A=tc5_(w6{KwTa#;`lrCen^fuz?%BUw6GJZL{zEsdQbB1?O>xUi#5tY-j0(~?#S=Y^;tF^3LKXSj&ZF8uyR6ChWd=7iKLRlQ+kAnvmHnu z2(bp#*bV)TIcF07;fsj9*LrHKTnH|ir^g<}k+HOK5Sc2DX}JT4&^JXr3U!*4_P+!q zdmwa?FJWdE#c*cdE@hN*!Kaqh$DpW!NH+Bhais1WC-6;R>IGE$*WaApCTg$q4N~w^ zTe#130N^5!oEx$zY^iPZ3n8d&?1!q*iOIk~+;lhJTT--{*J6O)m#4$0-)Z~o=Rl&%7NNcg+>vu9u-rs!_$m|!(OZ$pRg#w2vniLgR0D-T{NK56 zggX2(E%&WDdw1*2O~vIA(`%7M?4Ci&7gxpN0y2l8;_XeQa6o#gCG^cVyzN5~n2~D> z_bF@|XF`5NO>8^#T4*#Q$s^6ly4I04dACjZE7f|9GtHcCuGZrYh~AC{^sA1SgoK?d zcK&&Hn-8sHR*tm;)|fasd@uXx-4lF`S9kzy9}6$!?0guDx|FA+9n9Q;G%wl}c3|~Nt>N~e+Pl_yAT#hNs!4w#>m+l#6H8HzC&><@p#!;q15TD`-kVCf3 z@TE<$()a)XSE=voHzD$hUl|yk;`0ClfYPl9Ly%A}{IO2>@X&_1Gw<5GeD4?4u;E3)linxnEbKGM^oez*)=5ZA+X`X2jApERU2gX{GE_kxZ6c&5L`2_dz-NJ{Mw zgrn)qOHPlV*qoT+NSu17XavN_U(mfNikP7njFq`LLYkUbtCg#UJk}3B7+jdf?sK!E z-RU5D7>jw}lI2{Qcj|ZAwrE6Uc{+zisPM%RjAz^QtFK z%&>N_a}T0@`@@B4<8zhy^yE3e@4fMXszc=NxO0_{P-{v>?p8HGayaX0oiH@eF6L5FHXXIG{-ErYMy0)2Ts$HP=?V23hjcwNWD1V}w8sq{z@%C^ptLMr^}|EDLGH_>LT+%sHmzTb?O>So!Jxt?kHd2m%LclV ze>7a@;cKn(km!rtGlu5bn`D@c4bGmH65dU1stX+_o!@M?%Hzi3LW$E#*Z+5Jv$qqt zO-%(-w;nh1W3TbdVQ6d=kB{y4)stz5st^-<>@1BDzPxc%(Z1Tf!7Q`&5$6nEm^YP#|I>@&0|i=mg^ zP2!_Bq)U6>-M-P22_BzyO=yelDcxJJbhdib{5{ z{n*CQ#rA2NpRMY#%N2(p-)aFbbEHpd$S`#2d#At<{3RQsHEc*5B}DNNaj*A4x|O*d zbDBV)#3U;bxZe&GFG^4>1n$#Ev$cFk7hjdku;rldM$UPO8o_k3^w1F~L=$UuovIAE z@65;%&W46J&cHVlmPE;_I?pITBRXGMwoaF2`)iGPml>+&8T!2Ia}$%xJP9yIp~qrz zpXH3<)^5BOjcoRt8L(+%^)U)XJ*`_*zQ%S>(ni)|jG)svQn1f>?mJ?|ABy+CrOM>j zM$LR@%=t2zXvj+bfgC~d5;!BLr;wYh7aZLV&uJZTO!D#@CQ(ppZK0vi&F*oi#C4}% zSf@}XjkfzCK+p2&cBfsJ=Div7qInqyY)dGo@Z6=4@5w8gmtA5|_CiFi0RWF+hfGFk zz7|ROy2`MC98j-9@;j&mLMvdy#Bj^JDDhU?rtIB`!Bf0sr&GelUElKXBRXc1KMS*S zn0_8Ds9jLcG8pHi(jbuwd!|T>?Phl_o2Izt2xevE?4F^OB|N_fi~i;~85=4cv>`Ww z4MCQx2KaMOc;-9wB%<{R!_97SwH>RTDyYODNkFx7S{PwEX*Ndmf9-@vXsUF^<;%to zvu$+(u1zj5R|D8;LrrU0jkEDkZt_sv(IYJ-%&FPvZ-zyb@)Obv6Ajy1}n& zp5L*yVxHo@Gk8i>#<0=jTZHE@M&6TG+TpF@B>s{%zS!I`-~E-GHRw{RP{YcyFGyX@O0 z%YRw4xcbkVJE3$_l-NaeTVv9e&gX_PzOUx2i#fJXRxBl*CyBFU(v0LB zvlew(q$kR$6Y8#4LJvm$%GZgKUH_iC%Z2SAxbPmbKLs4-7tGs)5c$t}B;fEsa3VMa2_?u0^I9&OkQl5QW2+&iNk0&b^P?UUg80qc_qhYAR zkZ)w3FLuPMDA%tiOX6(&rmYwLla3#quS5Lmo^Fr=#%)O4pE{tF2vVCUhaANX zHmy#~d#WiMDFCu&0lwI)Z3Fp|Hzy8TPr;W>L9y+9YS52cB~lj3s$-7%XN=uCE-_b8 z^3zD>=M3I?HPW>QN5>`iyC0Ptq|baG8dnWknB~n>IsD?+ogH%U0BIZa-4W^3xM+a1 z4P9tMIyG&7!OPe_X)#>WlMI};a9E1F(7nUet>($cgPsjwAGyluR4tM(%a^D1YRbTyn%F5KJ-Bzk{Z_7mA7P(hoRlY5BI9QyGN(1yR-JcASqRi z@}AJy#pXRsr_O5IFCzulUHtlpn9tc@= z7RE0e-TuYh&L(hX^GAlq!Pw%3mED2G1~;RW_SJB`i3dXCj~Wls@!mE;J~Su7AI0$= z`$#glzg{mmikZakfqq0d#%FK!E52nQm1sA~bsCHD^lISp+v8c-QoaqkIAH1Rdr_=XyIgb_D*H-= zV4ZEUCd$mC{=<9+&Xir>Oj6l;<5aol*e+VO_gG;;zbVDaw?CS_d3GPtieBs55x$6i zUh~_7%9G!QB55tD+_h{fuew1&weRb;Ec5rCl73a26GMvEj_@pDHXoYiBpEg;?)Yq} zkA2zhRD|0bMCcmXtl?MJN>=0VzAdrT%epa|m`ApBHM%1tvfL8-)yMM(GjpmB7T#N9VZ2bBR zm?#?oSIj7t(_)z)@Kg4`*jZ|KFg4S~8~Uk%4%=MZ(mofk`xDbhp~|J0%r0*!r~e>3@nQ6wr@$ z-V3=F|FJez6cdO;G$EgQT9M-7S^aOAwc3ucmZ@0N2_Hg+MJErGyif1udZmUdmOz=UtH}83{_6q2k#{{{1V+D+I1rgAGfYlS+V6N<$#V1!a~v z`@xH8r<2JhbQ--%$8)nY>3rDB`>95J-lu-;^<3dWzuaaJp3)l2W1y4|GpQ-Gf{fl7 z3Tm@{n4ZD%xup4VnftW|*@zm{YK+pQVh(r$_EPJ&a}x@pnW05vT_<_q5_Hq->;bn?WG~@F$UqK)LuTBF&m(d%2+Gn{IMX6uS)lchGYKC~>5wDsWifZiSd`u0 zrWH24(c{u3PvNYy*IM0rE8f(TOK2wzWZ{;o!yx z4uD9p!M{}>t&kYXgF$z)zc2Y-A57XYchl#`n*+nAszIDXVw)O&&W;a2M8T72N+TJ3 zRQ)SPZdwrj)Bd>b7)FiX0#IJs+g6ZJ=_5}yD>jvGvyDD~eJtBkx=+Bk8-MzW{^=^~ z(Ei(YTh@QvoJMJryQqHL&PM6LRiHZVFr#$f&M_SKq)|F>Q#HQ*U%zoMU7Y>zS4u0T nt@NMk3vBydr#N`HCKl+`cnDo{_QVFZRr^C$e*! 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 literal 0 HcmV?d00001 diff --git a/docs/_media/flashdb_porting_layer.png b/docs/_media/flashdb_porting_layer.png new file mode 100644 index 0000000000000000000000000000000000000000..dd7aa5e82c263d8be4e52b5a9df3779269de9ded GIT binary patch literal 22319 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 L&(yxCSOonKOV4#i literal 0 HcmV?d00001 diff --git a/docs/zh/images/wechat_support.png b/docs/_media/wechat_support.png similarity index 100% rename from docs/zh/images/wechat_support.png rename to docs/_media/wechat_support.png diff --git a/docs/_navbar.md b/docs/_navbar.md new file mode 100644 index 0000000..b0181d6 --- /dev/null +++ b/docs/_navbar.md @@ -0,0 +1,3 @@ +- Translations + - [:cn: 中文](/zh-cn/) + - [:uk: English](/) \ No newline at end of file diff --git a/docs/_scripts/docsify.min.js b/docs/_scripts/docsify.min.js new file mode 100644 index 0000000..13ee722 --- /dev/null +++ b/docs/_scripts/docsify.min.js @@ -0,0 +1 @@ +!function(){function s(n){var r=Object.create(null);return function(e){var t=c(e)?e:JSON.stringify(e);return r[t]||(r[t]=n(e))}}var a=s(function(e){return e.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}),l=Object.prototype.hasOwnProperty,f=Object.assign||function(e){for(var t=arguments,n=1;n/gm),Ve=$(/^data-[\-\w.\u00B7-\uFFFF]/),Xe=$(/^aria-[\-\w]+$/),Ke=$(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),Qe=$(/^(?:\w+script|data):/i),Je=$(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g),et="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function tt(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t

').querySelector("svg img")&&(o=!0)}catch(e){}}(),function(){try{var e=$("</title><img>");ze(/<\/title/,e.querySelector("title").innerHTML)&&(s=!0)}catch(e){}}());function fe(e){return _.call(e.ownerDocument||e,e,r.SHOW_ELEMENT|r.SHOW_COMMENT|r.SHOW_TEXT,function(){return r.FILTER_ACCEPT},!1)}function he(e){return"object"===(void 0===f?"undefined":et(f))?e instanceof f:e&&"object"===(void 0===e?"undefined":et(e))&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName}function ge(e,t,n){E[e]&&we(E[e],function(e){e.call(d,t,n,pe)})}function me(e){var t=void 0;if(ge("beforeSanitizeElements",e,null),function(e){return!(e instanceof h||e instanceof g||"string"==typeof e.nodeName&&"string"==typeof e.textContent&&"function"==typeof e.removeChild&&e.attributes instanceof a&&"function"==typeof e.removeAttribute&&"function"==typeof e.setAttribute&&"string"==typeof e.namespaceURI)}(e))return R(e),!0;var n=Le(e.nodeName);if(ge("uponSanitizeElement",e,{tagName:n,allowedTags:j}),("svg"===n||"math"===n)&&0!==e.querySelectorAll("p, br").length)return R(e),!0;if(j[n]&&!U[n])return"noscript"===n&&ze(/<\/noscript/i,e.innerHTML)?(R(e),!0):"noembed"===n&&ze(/<\/noembed/i,e.innerHTML)?(R(e),!0):(!G||e.firstElementChild||e.content&&e.content.firstElementChild||!ze(/</g,e.textContent)||(Te(d.removed,{element:e.cloneNode()}),e.innerHTML?e.innerHTML=Ce(e.innerHTML,/</g,"<"):e.innerHTML=Ce(e.textContent,/</g,"<")),V&&3===e.nodeType&&(t=e.textContent,t=Ce(t,F," "),t=Ce(t,z," "),e.textContent!==t&&(Te(d.removed,{element:e.cloneNode()}),e.textContent=t)),ge("afterSanitizeElements",e,null),!1);if(ie&&!se[n]&&"function"==typeof e.insertAdjacentHTML)try{var r=e.innerHTML;e.insertAdjacentHTML("AfterEnd",y?y.createHTML(r):r)}catch(e){}return R(e),!0}function ve(e,t,n){if(re&&("id"===t||"name"===t)&&(n in l||n in de))return!1;if(W&&ze(O,t));else if(Z&&ze(M,t));else{if(!I[t]||B[t])return!1;if(ce[t]);else if(ze(D,Ce(n,P,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==$e(n,"data:")||!le[e])if(Y&&!ze(N,Ce(n,P,"")));else if(n)return!1}return!0}function be(e){var t=void 0,n=void 0,r=void 0,i=void 0,a=void 0;ge("beforeSanitizeAttributes",e,null);var o=e.attributes;if(o){var s={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:I};for(a=o.length;a--;){var l=t=o[a],c=l.name,u=l.namespaceURI;if(n=Fe(t.value),r=Le(c),s.attrName=r,s.attrValue=n,s.keepAttr=!0,s.forceKeepAttr=void 0,ge("uponSanitizeAttribute",e,s),n=s.attrValue,!s.forceKeepAttr){if("name"===r&&"IMG"===e.nodeName&&o.id)i=o.id,o=Ee(o,[]),C("id",e),C(c,e),_e(o,i)>a&&e.setAttribute("id",i.value);else{if("INPUT"===e.nodeName&&"type"===r&&"file"===n&&s.keepAttr&&(I[r]||!B[r]))continue;"id"===c&&e.setAttribute(c,""),C(c,e)}if(s.keepAttr)if(G&&ze(/\/>/i,n))C(c,e);else if(ze(/svg|math/i,e.namespaceURI)&&ze(Oe("</("+Se(ke(se),"|")+")","i"),n))C(c,e);else{V&&(n=Ce(n,F," "),n=Ce(n,z," "));var p=e.nodeName.toLowerCase();if(ve(p,r,n))try{u?e.setAttributeNS(u,c,n):e.setAttribute(c,n),Ae(d.removed)}catch(e){}}}}ge("afterSanitizeAttributes",e,null)}}function ye(e){var t=void 0,n=fe(e);for(ge("beforeSanitizeShadowDOM",e,null);t=n.nextNode();)ge("uponSanitizeShadowNode",t,null),me(t)||(t.content instanceof p&&ye(t.content),be(t));ge("afterSanitizeShadowDOM",e,null)}return d.sanitize=function(e,t){var n=void 0,r=void 0,i=void 0,a=void 0,o=void 0;if("string"!=typeof(e=e||"\x3c!--\x3e")&&!he(e)){if("function"!=typeof e.toString)throw Me("toString is not a function");if("string"!=typeof(e=e.toString()))throw Me("dirty is not a string, aborting")}if(!d.isSupported){if("object"===et(c.toStaticHTML)||"function"==typeof c.toStaticHTML){if("string"==typeof e)return c.toStaticHTML(e);if(he(e))return c.toStaticHTML(e.outerHTML)}return e}if(K||L(t),d.removed=[],"string"==typeof e&&(ae=!1),ae);else if(e instanceof f)1===(r=(n=$("\x3c!--\x3e")).ownerDocument.importNode(e,!0)).nodeType&&"BODY"===r.nodeName?n=r:"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!J&&!V&&!X&&ne&&-1===e.indexOf("<"))return y?y.createHTML(e):e;if(!(n=$(e)))return J?null:k}n&&Q&&R(n.firstChild);for(var s=fe(ae?e:n);i=s.nextNode();)3===i.nodeType&&i===a||me(i)||(i.content instanceof p&&ye(i.content),be(i),a=i);if(a=null,ae)return e;if(J){if(ee)for(o=A.call(n.ownerDocument);n.firstChild;)o.appendChild(n.firstChild);else o=n;return te&&(o=T.call(u,o,!0)),o}var l=X?n.outerHTML:n.innerHTML;return V&&(l=Ce(l,F," "),l=Ce(l,z," ")),y&&ne?y.createHTML(l):l},d.setConfig=function(e){L(e),K=!0},d.clearConfig=function(){pe=null,K=!1},d.isValidAttribute=function(e,t,n){pe||L({});var r=Le(e),i=Le(t);return ve(r,i,n)},d.addHook=function(e,t){"function"==typeof t&&(E[e]=E[e]||[],Te(E[e],t))},d.removeHook=function(e){E[e]&&Ae(E[e])},d.removeHooks=function(e){E[e]&&(E[e]=[])},d.removeAllHooks=function(){E={}},d}();function H(e){var t,n=e.loaded,r=e.total,i=e.step;P||function(){var e=g("div");e.classList.add("progress"),o(v,e),P=e}(),t=i?80<(t=parseInt(P.style.width||0,10)+i)?80:t:Math.floor(n/r*100),P.style.opacity=1,P.style.width=95<=t?"100%":t+"%",95<=t&&(clearTimeout(D),D=setTimeout(function(e){P.style.opacity=0,P.style.width="0%"},200))}var I={};function q(a,e,t){void 0===e&&(e=!1),void 0===t&&(t={});function n(){o.addEventListener.apply(o,arguments)}var o=new XMLHttpRequest,r=I[a];if(r)return{then:function(e){return e(r.content,r.opt)},abort:p};for(var i in o.open("GET",a),t)l.call(t,i)&&o.setRequestHeader(i,t[i]);return o.send(),{then:function(r,i){if(void 0===i&&(i=p),e){var t=setInterval(function(e){return H({step:Math.floor(5*Math.random()+1)})},500);n("progress",H),n("loadend",function(e){H(e),clearInterval(t)})}n("error",i),n("load",function(e){var t=e.target;if(400<=t.status)i(t);else{var n=I[a]={content:t.response,opt:{updatedAt:o.getResponseHeader("last-modified")}};r(n.content,n.opt)}})},abort:function(e){return 4!==o.readyState&&o.abort()}}}function U(e,t){e.innerHTML=e.innerHTML.replace(/var\(\s*--theme-color.*?\)/g,t)}function B(e,t,r,i){void 0===i&&(i=p);var a=e._hooks[t],o=function(t){var e=a[t];if(t>=a.length)i(r);else if("function"==typeof e)if(2===e.length)e(r,function(e){r=e,o(t+1)});else{var n=e(r);r=void 0===n?r:n,o(t+1)}else o(t+1)};o(0)}var Z=u.title;function W(){var e=m("section.cover");if(e){var t=e.getBoundingClientRect().height;window.pageYOffset>=t||e.classList.contains("hidden")?_(v,"add","sticky"):_(v,"remove","sticky")}}function Y(e,t,r,n){var i=[];null!=(t=m(t))&&(i=y(t,"a"));var a,o=decodeURI(e.toURL(e.getCurrentPath()));return i.sort(function(e,t){return t.href.length-e.href.length}).forEach(function(e){var t=e.getAttribute("href"),n=r?e.parentNode:e;e.title=e.innerText,0!==o.indexOf(t)||a?_(n,"remove","active"):(a=e,_(n,"add","active"))}),n&&(u.title=a?a.title||a.innerText+" - "+Z:Z),a}var G=decodeURIComponent,V=encodeURIComponent;function X(e){var n={};return(e=e.trim().replace(/^(\?|#|&)/,""))&&e.split("&").forEach(function(e){var t=e.replace(/\+/g," ").split("=");n[t[0]]=t[1]&&G(t[1])}),n}function K(e,t){void 0===t&&(t=[]);var n=[];for(var r in e)-1<t.indexOf(r)||n.push(e[r]?(V(r)+"="+V(e[r])).toLowerCase():V(r));return n.length?"?"+n.join("&"):""}var Q=s(function(e){return/(:|(\/{2}))/g.test(e)}),J=s(function(e){return e.split(/[?#]/)[0]}),ee=s(function(e){if(/\/$/g.test(e))return e;var t=e.match(/(\S*\/)[^/]+$/);return t?t[1]:""}),te=s(function(e){return e.replace(/^\/+/,"/").replace(/([^:])\/{2,}/g,"$1/")}),ne=s(function(e){for(var t=e.replace(/^\//,"").split("/"),n=[],r=0,i=t.length;r<i;r++){var a=t[r];".."===a?n.pop():"."!==a&&n.push(a)}return"/"+n.join("/")});function re(){for(var e=[],t=arguments.length;t--;)e[t]=arguments[t];return te(e.join("/"))}var ie=s(function(e){return e.replace("#","?id=")});function ae(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}var oe=(function(e,t,n){return t&&ae(e.prototype,t),n&&ae(e,n),e}(se,[{key:"getIntermediateValue",value:function(e){return this.decimal?e:Math.round(e)}},{key:"getFinalValue",value:function(){return this.end}}]),se);function se(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,se),this.start=e.start,this.end=e.end,this.decimal=e.decimal}function le(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}var ce=(function(e,t,n){return t&&le(e.prototype,t),n&&le(e,n),e}(ue,[{key:"begin",value:function(){return this.isRunning||this.next===this.end||(this.frame=window.requestAnimationFrame(this._tick.bind(this))),this}},{key:"stop",value:function(){return window.cancelAnimationFrame(this.frame),this.isRunning=!1,this.frame=null,this.timeStart=null,this.next=null,this}},{key:"on",value:function(e,t){return this.events[e]=this.events[e]||[],this.events[e].push(t),this}},{key:"_emit",value:function(e,t){var n=this,r=this.events[e];r&&r.forEach(function(e){return e.call(n,t)})}},{key:"_tick",value:function(e){this.isRunning=!0;var t=this.next||this.start;this.timeStart||(this.timeStart=e),this.timeElapsed=e-this.timeStart,this.next=this.ease(this.timeElapsed,this.start,this.end-this.start,this.duration),this._shouldTick(t)?(this._emit("tick",this.tweener.getIntermediateValue(this.next)),this.frame=window.requestAnimationFrame(this._tick.bind(this))):(this._emit("tick",this.tweener.getFinalValue()),this._emit("done",null))}},{key:"_shouldTick",value:function(e){return{up:this.next<this.end&&e<=this.next,down:this.next>this.end&&e>=this.next}[this.direction]}},{key:"_defaultEase",value:function(e,t,n,r){return(e/=r/2)<1?n/2*e*e+t:-n/2*(--e*(e-2)-1)+t}}]),ue);function ue(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,ue),this.duration=e.duration||1e3,this.ease=e.easing||this._defaultEase,this.tweener=e.tweener||new oe(e),this.start=this.tweener.start,this.end=this.tweener.end,this.frame=null,this.next=null,this.isRunning=!1,this.events={},this.direction=this.start<this.end?"up":"down"}var pe={},de=!1,fe=null,he=!0,ge=0;function me(e){if(he){for(var t,n=m(".sidebar"),r=y(".anchor"),i=b(n,".sidebar-nav"),a=b(n,"li.active"),o=document.documentElement,s=(o&&o.scrollTop||document.body.scrollTop)-ge,l=0,c=r.length;l<c;l+=1){var u=r[l];if(u.offsetTop>s){t=t||u;break}t=u}if(t){var p=pe[ve(e,t.getAttribute("data-id"))];if(p&&p!==a&&(a&&a.classList.remove("active"),p.classList.add("active"),a=p,!de&&v.classList.contains("sticky"))){var d=n.clientHeight,f=a.offsetTop+a.clientHeight+40,h=a.offsetTop>=i.scrollTop&&f<=i.scrollTop+d,g=f-0<d;n.scrollTop=h?i.scrollTop:g?0:f-d}}}}function ve(e,t){return decodeURIComponent(e)+"?id="+decodeURIComponent(t)}function be(e,t){if(t){var n=A().topMargin,r=b("#"+t);r&&function(e,t){void 0===t&&(t=0),fe&&fe.stop(),he=!1,fe=new ce({start:window.pageYOffset,end:e.getBoundingClientRect().top+window.pageYOffset-t,duration:500}).on("tick",function(e){return window.scrollTo(0,e)}).on("done",function(){he=!0,fe=null}).begin()}(r,n);var i=pe[ve(e,t)],a=b(m(".sidebar"),"li.active");a&&a.classList.remove("active"),i&&i.classList.add("active")}}var ye=u.scrollingElement||u.documentElement;var it="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function at(e,t){return e(t={exports:{}},t.exports),t.exports}function ot(e){return dt[e]}var st=at(function(t){function e(){return{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tokenizer:null,walkTokens:null,xhtml:!1}}t.exports={defaults:{baseUrl:null,breaks:!1,gfm:!0,headerIds:!0,headerPrefix:"",highlight:null,langPrefix:"language-",mangle:!0,pedantic:!1,renderer:null,sanitize:!1,sanitizer:null,silent:!1,smartLists:!1,smartypants:!1,tokenizer:null,walkTokens:null,xhtml:!1},getDefaults:e,changeDefaults:function(e){t.exports.defaults=e}}}),lt=(st.defaults,st.getDefaults,st.changeDefaults,/[&<>"']/),ct=/[&<>"']/g,ut=/[<>"']|&(?!#?\w+;)/,pt=/[<>"']|&(?!#?\w+;)/g,dt={"&":"&","<":"<",">":">",'"':""","'":"'"};var ft=/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi;function ht(e){return e.replace(ft,function(e,t){return"colon"===(t=t.toLowerCase())?":":"#"===t.charAt(0)?"x"===t.charAt(1)?String.fromCharCode(parseInt(t.substring(2),16)):String.fromCharCode(+t.substring(1)):""})}var gt=/(^|[^\[])\^/g;var mt=/[^\w:]/g,vt=/^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;var bt={},yt=/^[^:]+:\/*[^/]*$/,kt=/^([^:]+:)[\s\S]*$/,xt=/^([^:]+:\/*[^/]*)[\s\S]*$/;function wt(e,t){bt[" "+e]||(yt.test(e)?bt[" "+e]=e+"/":bt[" "+e]=_t(e,"/",!0));var n=-1===(e=bt[" "+e]).indexOf(":");return"//"===t.substring(0,2)?n?t:e.replace(kt,"$1")+t:"/"===t.charAt(0)?n?t:e.replace(xt,"$1")+t:e+t}function _t(e,t,n){var r=e.length;if(0===r)return"";for(var i=0;i<r;){var a=e.charAt(r-i-1);if(a!==t||n){if(a===t||!n)break;i++}else i++}return e.substr(0,r-i)}var St=function(e,t){if(t){if(lt.test(e))return e.replace(ct,ot)}else if(ut.test(e))return e.replace(pt,ot);return e},At=ht,Tt=function(n,e){n=n.source||n,e=e||"";var r={replace:function(e,t){return t=(t=t.source||t).replace(gt,"$1"),n=n.replace(e,t),r},getRegex:function(){return new RegExp(n,e)}};return r},Et=function(e,t,n){if(e){var r;try{r=decodeURIComponent(ht(n)).replace(mt,"").toLowerCase()}catch(e){return null}if(0===r.indexOf("javascript:")||0===r.indexOf("vbscript:")||0===r.indexOf("data:"))return null}t&&!vt.test(n)&&(n=wt(t,n));try{n=encodeURI(n).replace(/%25/g,"%")}catch(e){return null}return n},Lt={exec:function(){}},Rt=function(e){for(var t,n,r=arguments,i=1;i<arguments.length;i++)for(n in t=r[i])Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n]);return e},Ct=function(e,t){var n=e.replace(/\|/g,function(e,t,n){for(var r=!1,i=t;0<=--i&&"\\"===n[i];)r=!r;return r?"|":" |"}).split(/ \|/),r=0;if(n.length>t)n.splice(t);else for(;n.length<t;)n.push("");for(;r<n.length;r++)n[r]=n[r].trim().replace(/\\\|/g,"|");return n},$t=_t,Ft=function(e,t){if(-1===e.indexOf(t[1]))return-1;for(var n=e.length,r=0,i=0;i<n;i++)if("\\"===e[i])i++;else if(e[i]===t[0])r++;else if(e[i]===t[1]&&--r<0)return i;return-1},zt=function(e){e&&e.sanitize&&!e.silent&&console.warn("marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options")},Ot=st.defaults,Mt=$t,Nt=Ct,Pt=St,Dt=Ft;function jt(e,t,n){var r=t.href,i=t.title?Pt(t.title):null,a=e[1].replace(/\\([\[\]])/g,"$1");return"!"!==e[0].charAt(0)?{type:"link",raw:n,href:r,title:i,text:a}:{type:"image",raw:n,href:r,title:i,text:Pt(a)}}var Ht=function(){function e(e){this.options=e||Ot}return e.prototype.space=function(e){var t=this.rules.block.newline.exec(e);if(t)return 1<t[0].length?{type:"space",raw:t[0]}:{raw:"\n"}},e.prototype.code=function(e,t){var n=this.rules.block.code.exec(e);if(n){var r=t[t.length-1];if(r&&"paragraph"===r.type)return{raw:n[0],text:n[0].trimRight()};var i=n[0].replace(/^ {4}/gm,"");return{type:"code",raw:n[0],codeBlockStyle:"indented",text:this.options.pedantic?i:Mt(i,"\n")}}},e.prototype.fences=function(e){var t=this.rules.block.fences.exec(e);if(t){var n=t[0],r=function(e,t){var n=e.match(/^(\s+)(?:```)/);if(null===n)return t;var r=n[1];return t.split("\n").map(function(e){var t=e.match(/^\s+/);return null===t?e:t[0].length>=r.length?e.slice(r.length):e}).join("\n")}(n,t[3]||"");return{type:"code",raw:n,lang:t[2]?t[2].trim():t[2],text:r}}},e.prototype.heading=function(e){var t=this.rules.block.heading.exec(e);if(t)return{type:"heading",raw:t[0],depth:t[1].length,text:t[2]}},e.prototype.nptable=function(e){var t=this.rules.block.nptable.exec(e);if(t){var n={type:"table",header:Nt(t[1].replace(/^ *| *\| *$/g,"")),align:t[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:t[3]?t[3].replace(/\n$/,"").split("\n"):[],raw:t[0]};if(n.header.length===n.align.length){var r,i=n.align.length;for(r=0;r<i;r++)/^ *-+: *$/.test(n.align[r])?n.align[r]="right":/^ *:-+: *$/.test(n.align[r])?n.align[r]="center":/^ *:-+ *$/.test(n.align[r])?n.align[r]="left":n.align[r]=null;for(i=n.cells.length,r=0;r<i;r++)n.cells[r]=Nt(n.cells[r],n.header.length);return n}}},e.prototype.hr=function(e){var t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:t[0]}},e.prototype.blockquote=function(e){var t=this.rules.block.blockquote.exec(e);if(t){var n=t[0].replace(/^ *> ?/gm,"");return{type:"blockquote",raw:t[0],text:n}}},e.prototype.list=function(e){var t=this.rules.block.list.exec(e);if(t){for(var n,r,i,a,o,s,l,c=t[0],u=t[2],p=1<u.length,d=")"===u[u.length-1],f={type:"list",raw:c,ordered:p,start:p?+u.slice(0,-1):"",loose:!1,items:[]},h=t[0].match(this.rules.block.item),g=!1,m=h.length,v=0;v<m;v++)r=(c=n=h[v]).length,~(n=n.replace(/^ *([*+-]|\d+[.)]) */,"")).indexOf("\n ")&&(r-=n.length,n=this.options.pedantic?n.replace(/^ {1,4}/gm,""):n.replace(new RegExp("^ {1,"+r+"}","gm"),"")),v!==m-1&&(i=this.rules.block.bullet.exec(h[v+1])[0],(p?1===i.length||!d&&")"===i[i.length-1]:1<i.length||this.options.smartLists&&i!==u)&&(a=h.slice(v+1).join("\n"),f.raw=f.raw.substring(0,f.raw.length-a.length),v=m-1)),o=g||/\n\n(?!\s*$)/.test(n),v!==m-1&&(g="\n"===n.charAt(n.length-1),o=o||g),o&&(f.loose=!0),l=void 0,(s=/^\[[ xX]\] /.test(n))&&(l=" "!==n[1],n=n.replace(/^\[[ xX]\] +/,"")),f.items.push({type:"list_item",raw:c,task:s,checked:l,loose:o,text:n});return f}},e.prototype.html=function(e){var t=this.rules.block.html.exec(e);if(t)return{type:this.options.sanitize?"paragraph":"html",raw:t[0],pre:!this.options.sanitizer&&("pre"===t[1]||"script"===t[1]||"style"===t[1]),text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(t[0]):Pt(t[0]):t[0]}},e.prototype.def=function(e){var t=this.rules.block.def.exec(e);if(t)return t[3]&&(t[3]=t[3].substring(1,t[3].length-1)),{tag:t[1].toLowerCase().replace(/\s+/g," "),raw:t[0],href:t[2],title:t[3]}},e.prototype.table=function(e){var t=this.rules.block.table.exec(e);if(t){var n={type:"table",header:Nt(t[1].replace(/^ *| *\| *$/g,"")),align:t[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:t[3]?t[3].replace(/\n$/,"").split("\n"):[]};if(n.header.length===n.align.length){n.raw=t[0];var r,i=n.align.length;for(r=0;r<i;r++)/^ *-+: *$/.test(n.align[r])?n.align[r]="right":/^ *:-+: *$/.test(n.align[r])?n.align[r]="center":/^ *:-+ *$/.test(n.align[r])?n.align[r]="left":n.align[r]=null;for(i=n.cells.length,r=0;r<i;r++)n.cells[r]=Nt(n.cells[r].replace(/^ *\| *| *\| *$/g,""),n.header.length);return n}}},e.prototype.lheading=function(e){var t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1]}},e.prototype.paragraph=function(e){var t=this.rules.block.paragraph.exec(e);if(t)return{type:"paragraph",raw:t[0],text:"\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1]}},e.prototype.text=function(e,t){var n=this.rules.block.text.exec(e);if(n){var r=t[t.length-1];return r&&"text"===r.type?{raw:n[0],text:n[0]}:{type:"text",raw:n[0],text:n[0]}}},e.prototype.escape=function(e){var t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:Pt(t[1])}},e.prototype.tag=function(e,t,n){var r=this.rules.inline.tag.exec(e);if(r)return!t&&/^<a /i.test(r[0])?t=!0:t&&/^<\/a>/i.test(r[0])&&(t=!1),!n&&/^<(pre|code|kbd|script)(\s|>)/i.test(r[0])?n=!0:n&&/^<\/(pre|code|kbd|script)(\s|>)/i.test(r[0])&&(n=!1),{type:this.options.sanitize?"text":"html",raw:r[0],inLink:t,inRawBlock:n,text:this.options.sanitize?this.options.sanitizer?this.options.sanitizer(r[0]):Pt(r[0]):r[0]}},e.prototype.link=function(e){var t=this.rules.inline.link.exec(e);if(t){var n=Dt(t[2],"()");if(-1<n){var r=(0===t[0].indexOf("!")?5:4)+t[1].length+n;t[2]=t[2].substring(0,n),t[0]=t[0].substring(0,r).trim(),t[3]=""}var i=t[2],a="";if(this.options.pedantic){var o=/^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(i);a=o?(i=o[1],o[3]):""}else a=t[3]?t[3].slice(1,-1):"";return jt(t,{href:(i=i.trim().replace(/^<([\s\S]*)>$/,"$1"))?i.replace(this.rules.inline._escapes,"$1"):i,title:a?a.replace(this.rules.inline._escapes,"$1"):a},t[0])}},e.prototype.reflink=function(e,t){var n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){var r=(n[2]||n[1]).replace(/\s+/g," ");if((r=t[r.toLowerCase()])&&r.href)return jt(n,r,n[0]);var i=n[0].charAt(0);return{type:"text",raw:i,text:i}}},e.prototype.strong=function(e,t,n){void 0===n&&(n="");var r=this.rules.inline.strong.start.exec(e);if(r&&(!r[1]||r[1]&&(""===n||this.rules.inline.punctuation.exec(n)))){t=t.slice(-1*e.length);var i,a="**"===r[0]?this.rules.inline.strong.endAst:this.rules.inline.strong.endUnd;for(a.lastIndex=0;null!=(r=a.exec(t));)if(i=this.rules.inline.strong.middle.exec(t.slice(0,r.index+3)))return{type:"strong",raw:e.slice(0,i[0].length),text:e.slice(2,i[0].length-2)}}},e.prototype.em=function(e,t,n){void 0===n&&(n="");var r=this.rules.inline.em.start.exec(e);if(r&&(!r[1]||r[1]&&(""===n||this.rules.inline.punctuation.exec(n)))){t=t.slice(-1*e.length);var i,a="*"===r[0]?this.rules.inline.em.endAst:this.rules.inline.em.endUnd;for(a.lastIndex=0;null!=(r=a.exec(t));)if(i=this.rules.inline.em.middle.exec(t.slice(0,r.index+2)))return{type:"em",raw:e.slice(0,i[0].length),text:e.slice(1,i[0].length-1)}}},e.prototype.codespan=function(e){var t=this.rules.inline.code.exec(e);if(t){var n=t[2].replace(/\n/g," "),r=/[^ ]/.test(n),i=n.startsWith(" ")&&n.endsWith(" ");return r&&i&&(n=n.substring(1,n.length-1)),n=Pt(n,!0),{type:"codespan",raw:t[0],text:n}}},e.prototype.br=function(e){var t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}},e.prototype.del=function(e){var t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[1]}},e.prototype.autolink=function(e,t){var n,r,i=this.rules.inline.autolink.exec(e);if(i)return r="@"===i[2]?"mailto:"+(n=Pt(this.options.mangle?t(i[1]):i[1])):n=Pt(i[1]),{type:"link",raw:i[0],text:n,href:r,tokens:[{type:"text",raw:n,text:n}]}},e.prototype.url=function(e,t){var n;if(n=this.rules.inline.url.exec(e)){var r,i;if("@"===n[2])i="mailto:"+(r=Pt(this.options.mangle?t(n[0]):n[0]));else{for(var a;a=n[0],n[0]=this.rules.inline._backpedal.exec(n[0])[0],a!==n[0];);r=Pt(n[0]),i="www."===n[1]?"http://"+r:r}return{type:"link",raw:n[0],text:r,href:i,tokens:[{type:"text",raw:r,text:r}]}}},e.prototype.inlineText=function(e,t,n){var r,i=this.rules.inline.text.exec(e);if(i)return r=t?this.options.sanitize?this.options.sanitizer?this.options.sanitizer(i[0]):Pt(i[0]):i[0]:Pt(this.options.smartypants?n(i[0]):i[0]),{type:"text",raw:i[0],text:r}},e}(),It=Lt,qt=Tt,Ut=Rt,Bt={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:/^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,hr:/^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,heading:/^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,blockquote:/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,list:/^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:"^ {0,3}(?:<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?\\?>\\n*|<![A-Z][\\s\\S]*?>\\n*|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$))",def:/^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,nptable:It,table:It,lheading:/^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,_paragraph:/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,text:/^[^\n]+/,_label:/(?!\s*\])(?:\\[\[\]]|[^\[\]])+/,_title:/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/};Bt.def=qt(Bt.def).replace("label",Bt._label).replace("title",Bt._title).getRegex(),Bt.bullet=/(?:[*+-]|\d{1,9}[.)])/,Bt.item=/^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/,Bt.item=qt(Bt.item,"gm").replace(/bull/g,Bt.bullet).getRegex(),Bt.list=qt(Bt.list).replace(/bull/g,Bt.bullet).replace("hr","\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))").replace("def","\\n+(?="+Bt.def.source+")").getRegex(),Bt._tag="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Bt._comment=/<!--(?!-?>)[\s\S]*?-->/,Bt.html=qt(Bt.html,"i").replace("comment",Bt._comment).replace("tag",Bt._tag).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),Bt.paragraph=qt(Bt._paragraph).replace("hr",Bt.hr).replace("heading"," {0,3}#{1,6} ").replace("|lheading","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",Bt._tag).getRegex(),Bt.blockquote=qt(Bt.blockquote).replace("paragraph",Bt.paragraph).getRegex(),Bt.normal=Ut({},Bt),Bt.gfm=Ut({},Bt.normal,{nptable:"^ *([^|\\n ].*\\|.*)\\n *([-:]+ *\\|[-| :]*)(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)",table:"^ *\\|(.+)\\n *\\|?( *[-:]+[-| :]*)(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)"}),Bt.gfm.nptable=qt(Bt.gfm.nptable).replace("hr",Bt.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",Bt._tag).getRegex(),Bt.gfm.table=qt(Bt.gfm.table).replace("hr",Bt.hr).replace("heading"," {0,3}#{1,6} ").replace("blockquote"," {0,3}>").replace("code"," {4}[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)").replace("tag",Bt._tag).getRegex(),Bt.pedantic=Ut({},Bt.normal,{html:qt("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:\"[^\"]*\"|'[^']*'|\\s[^'\"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",Bt._comment).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,fences:It,paragraph:qt(Bt.normal._paragraph).replace("hr",Bt.hr).replace("heading"," *#{1,6} *[^\n]").replace("lheading",Bt.lheading).replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").getRegex()});var Zt={escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,autolink:/^<(scheme:[^\s\x00-\x1f<>]*|email)>/,url:It,tag:"^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>",link:/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,reflink:/^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,nolink:/^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,reflinkSearch:"reflink|nolink(?!\\()",strong:{start:/^(?:(\*\*(?=[*punctuation]))|\*\*)(?![\s])|__/,middle:/^\*\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*\*$|^__(?![\s])((?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?)__$/,endAst:/[^punctuation\s]\*\*(?!\*)|[punctuation]\*\*(?!\*)(?:(?=[punctuation\s]|$))/,endUnd:/[^\s]__(?!_)(?:(?=[punctuation\s])|$)/},em:{start:/^(?:(\*(?=[punctuation]))|\*)(?![*\s])|_/,middle:/^\*(?:(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)|\*(?:(?!overlapSkip)(?:[^*]|\\\*)|overlapSkip)*?\*)+?\*$|^_(?![_\s])(?:(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)|_(?:(?!overlapSkip)(?:[^_]|\\_)|overlapSkip)*?_)+?_$/,endAst:/[^punctuation\s]\*(?!\*)|[punctuation]\*(?!\*)(?:(?=[punctuation\s]|$))/,endUnd:/[^\s]_(?!_)(?:(?=[punctuation\s])|$)/},code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,br:/^( {2,}|\\)\n(?!\s*$)/,del:It,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n))|(?= {2,}\n))/,punctuation:/^([\s*punctuation])/,_punctuation:"!\"#$%&'()+\\-.,/:;<=>?@\\[\\]`^{|}~"};Zt.punctuation=qt(Zt.punctuation).replace(/punctuation/g,Zt._punctuation).getRegex(),Zt._blockSkip="\\[[^\\]]*?\\]\\([^\\)]*?\\)|`[^`]*?`|<[^>]*?>",Zt._overlapSkip="__[^_]*?__|\\*\\*\\[^\\*\\]*?\\*\\*",Zt.em.start=qt(Zt.em.start).replace(/punctuation/g,Zt._punctuation).getRegex(),Zt.em.middle=qt(Zt.em.middle).replace(/punctuation/g,Zt._punctuation).replace(/overlapSkip/g,Zt._overlapSkip).getRegex(),Zt.em.endAst=qt(Zt.em.endAst,"g").replace(/punctuation/g,Zt._punctuation).getRegex(),Zt.em.endUnd=qt(Zt.em.endUnd,"g").replace(/punctuation/g,Zt._punctuation).getRegex(),Zt.strong.start=qt(Zt.strong.start).replace(/punctuation/g,Zt._punctuation).getRegex(),Zt.strong.middle=qt(Zt.strong.middle).replace(/punctuation/g,Zt._punctuation).replace(/blockSkip/g,Zt._blockSkip).getRegex(),Zt.strong.endAst=qt(Zt.strong.endAst,"g").replace(/punctuation/g,Zt._punctuation).getRegex(),Zt.strong.endUnd=qt(Zt.strong.endUnd,"g").replace(/punctuation/g,Zt._punctuation).getRegex(),Zt.blockSkip=qt(Zt._blockSkip,"g").getRegex(),Zt.overlapSkip=qt(Zt._overlapSkip,"g").getRegex(),Zt._escapes=/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g,Zt._scheme=/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/,Zt._email=/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/,Zt.autolink=qt(Zt.autolink).replace("scheme",Zt._scheme).replace("email",Zt._email).getRegex(),Zt._attribute=/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/,Zt.tag=qt(Zt.tag).replace("comment",Bt._comment).replace("attribute",Zt._attribute).getRegex(),Zt._label=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Zt._href=/<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/,Zt._title=/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/,Zt.link=qt(Zt.link).replace("label",Zt._label).replace("href",Zt._href).replace("title",Zt._title).getRegex(),Zt.reflink=qt(Zt.reflink).replace("label",Zt._label).getRegex(),Zt.reflinkSearch=qt(Zt.reflinkSearch,"g").replace("reflink",Zt.reflink).replace("nolink",Zt.nolink).getRegex(),Zt.normal=Ut({},Zt),Zt.pedantic=Ut({},Zt.normal,{strong:{start:/^__|\*\*/,middle:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,endAst:/\*\*(?!\*)/g,endUnd:/__(?!_)/g},em:{start:/^_|\*/,middle:/^()\*(?=\S)([\s\S]*?\S)\*(?!\*)|^_(?=\S)([\s\S]*?\S)_(?!_)/,endAst:/\*(?!\*)/g,endUnd:/_(?!_)/g},link:qt(/^!?\[(label)\]\((.*?)\)/).replace("label",Zt._label).getRegex(),reflink:qt(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",Zt._label).getRegex()}),Zt.gfm=Ut({},Zt.normal,{escape:qt(Zt.escape).replace("])","~|])").getRegex(),_extended_email:/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,url:/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,_backpedal:/(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,del:/^~+(?=\S)([\s\S]*?\S)~+/,text:/^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?= {2,}\n|[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/}),Zt.gfm.url=qt(Zt.gfm.url,"i").replace("email",Zt.gfm._extended_email).getRegex(),Zt.breaks=Ut({},Zt.gfm,{br:qt(Zt.br).replace("{2,}","*").getRegex(),text:qt(Zt.gfm.text).replace("\\b_","\\b_| {2,}\\n").replace(/\{2,\}/g,"*").getRegex()});var Wt={block:Bt,inline:Zt},Yt=st.defaults,Gt=Wt.block,Vt=Wt.inline;function Xt(e){return e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…")}function Kt(e){var t,n,r="",i=e.length;for(t=0;t<i;t++)n=e.charCodeAt(t),.5<Math.random()&&(n="x"+n.toString(16)),r+="&#"+n+";";return r}var Qt=function(){function n(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||Yt,this.options.tokenizer=this.options.tokenizer||new Ht,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options;var t={block:Gt.normal,inline:Vt.normal};this.options.pedantic?(t.block=Gt.pedantic,t.inline=Vt.pedantic):this.options.gfm&&(t.block=Gt.gfm,this.options.breaks?t.inline=Vt.breaks:t.inline=Vt.gfm),this.tokenizer.rules=t}var e={rules:{configurable:!0}};return e.rules.get=function(){return{block:Gt,inline:Vt}},n.lex=function(e,t){return new n(t).lex(e)},n.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," "),this.blockTokens(e,this.tokens,!0),this.inline(this.tokens),this.tokens},n.prototype.blockTokens=function(e,t,n){var r,i,a,o;for(void 0===t&&(t=[]),void 0===n&&(n=!0),e=e.replace(/^ +$/gm,"");e;)if(r=this.tokenizer.space(e))e=e.substring(r.raw.length),r.type&&t.push(r);else if(r=this.tokenizer.code(e,t))e=e.substring(r.raw.length),r.type?t.push(r):((o=t[t.length-1]).raw+="\n"+r.raw,o.text+="\n"+r.text);else if(r=this.tokenizer.fences(e))e=e.substring(r.raw.length),t.push(r);else if(r=this.tokenizer.heading(e))e=e.substring(r.raw.length),t.push(r);else if(r=this.tokenizer.nptable(e))e=e.substring(r.raw.length),t.push(r);else if(r=this.tokenizer.hr(e))e=e.substring(r.raw.length),t.push(r);else if(r=this.tokenizer.blockquote(e))e=e.substring(r.raw.length),r.tokens=this.blockTokens(r.text,[],n),t.push(r);else if(r=this.tokenizer.list(e)){for(e=e.substring(r.raw.length),a=r.items.length,i=0;i<a;i++)r.items[i].tokens=this.blockTokens(r.items[i].text,[],!1);t.push(r)}else if(r=this.tokenizer.html(e))e=e.substring(r.raw.length),t.push(r);else if(n&&(r=this.tokenizer.def(e)))e=e.substring(r.raw.length),this.tokens.links[r.tag]||(this.tokens.links[r.tag]={href:r.href,title:r.title});else if(r=this.tokenizer.table(e))e=e.substring(r.raw.length),t.push(r);else if(r=this.tokenizer.lheading(e))e=e.substring(r.raw.length),t.push(r);else if(n&&(r=this.tokenizer.paragraph(e)))e=e.substring(r.raw.length),t.push(r);else if(r=this.tokenizer.text(e,t))e=e.substring(r.raw.length),r.type?t.push(r):((o=t[t.length-1]).raw+="\n"+r.raw,o.text+="\n"+r.text);else if(e){var s="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(s);break}throw new Error(s)}return t},n.prototype.inline=function(e){var t,n,r,i,a,o,s=e.length;for(t=0;t<s;t++)switch((o=e[t]).type){case"paragraph":case"text":case"heading":o.tokens=[],this.inlineTokens(o.text,o.tokens);break;case"table":for(o.tokens={header:[],cells:[]},i=o.header.length,n=0;n<i;n++)o.tokens.header[n]=[],this.inlineTokens(o.header[n],o.tokens.header[n]);for(i=o.cells.length,n=0;n<i;n++)for(a=o.cells[n],o.tokens.cells[n]=[],r=0;r<a.length;r++)o.tokens.cells[n][r]=[],this.inlineTokens(a[r],o.tokens.cells[n][r]);break;case"blockquote":this.inline(o.tokens);break;case"list":for(i=o.items.length,n=0;n<i;n++)this.inline(o.items[n].tokens)}return e},n.prototype.inlineTokens=function(e,t,n,r,i){var a;void 0===t&&(t=[]),void 0===n&&(n=!1),void 0===r&&(r=!1),void 0===i&&(i="");var o,s=e;if(this.tokens.links){var l=Object.keys(this.tokens.links);if(0<l.length)for(;null!=(o=this.tokenizer.rules.inline.reflinkSearch.exec(s));)l.includes(o[0].slice(o[0].lastIndexOf("[")+1,-1))&&(s=s.slice(0,o.index)+"["+"a".repeat(o[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(o=this.tokenizer.rules.inline.blockSkip.exec(s));)s=s.slice(0,o.index)+"["+"a".repeat(o[0].length-2)+"]"+s.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;e;)if(a=this.tokenizer.escape(e))e=e.substring(a.raw.length),t.push(a);else if(a=this.tokenizer.tag(e,n,r))e=e.substring(a.raw.length),n=a.inLink,r=a.inRawBlock,t.push(a);else if(a=this.tokenizer.link(e))e=e.substring(a.raw.length),"link"===a.type&&(a.tokens=this.inlineTokens(a.text,[],!0,r)),t.push(a);else if(a=this.tokenizer.reflink(e,this.tokens.links))e=e.substring(a.raw.length),"link"===a.type&&(a.tokens=this.inlineTokens(a.text,[],!0,r)),t.push(a);else if(a=this.tokenizer.strong(e,s,i))e=e.substring(a.raw.length),a.tokens=this.inlineTokens(a.text,[],n,r),t.push(a);else if(a=this.tokenizer.em(e,s,i))e=e.substring(a.raw.length),a.tokens=this.inlineTokens(a.text,[],n,r),t.push(a);else if(a=this.tokenizer.codespan(e))e=e.substring(a.raw.length),t.push(a);else if(a=this.tokenizer.br(e))e=e.substring(a.raw.length),t.push(a);else if(a=this.tokenizer.del(e))e=e.substring(a.raw.length),a.tokens=this.inlineTokens(a.text,[],n,r),t.push(a);else if(a=this.tokenizer.autolink(e,Kt))e=e.substring(a.raw.length),t.push(a);else if(n||!(a=this.tokenizer.url(e,Kt))){if(a=this.tokenizer.inlineText(e,r,Xt))e=e.substring(a.raw.length),i=a.raw.slice(-1),t.push(a);else if(e){var c="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(c);break}throw new Error(c)}}else e=e.substring(a.raw.length),t.push(a);return t},Object.defineProperties(n,e),n}(),Jt=st.defaults,en=Et,tn=St,nn=function(){function e(e){this.options=e||Jt}return e.prototype.code=function(e,t,n){var r=(t||"").match(/\S*/)[0];if(this.options.highlight){var i=this.options.highlight(e,r);null!=i&&i!==e&&(n=!0,e=i)}return r?'<pre><code class="'+this.options.langPrefix+tn(r,!0)+'">'+(n?e:tn(e,!0))+"</code></pre>\n":"<pre><code>"+(n?e:tn(e,!0))+"</code></pre>\n"},e.prototype.blockquote=function(e){return"<blockquote>\n"+e+"</blockquote>\n"},e.prototype.html=function(e){return e},e.prototype.heading=function(e,t,n,r){return this.options.headerIds?"<h"+t+' id="'+this.options.headerPrefix+r.slug(n)+'">'+e+"</h"+t+">\n":"<h"+t+">"+e+"</h"+t+">\n"},e.prototype.hr=function(){return this.options.xhtml?"<hr/>\n":"<hr>\n"},e.prototype.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+"</"+r+">\n"},e.prototype.listitem=function(e){return"<li>"+e+"</li>\n"},e.prototype.checkbox=function(e){return"<input "+(e?'checked="" ':"")+'disabled="" type="checkbox"'+(this.options.xhtml?" /":"")+"> "},e.prototype.paragraph=function(e){return"<p>"+e+"</p>\n"},e.prototype.table=function(e,t){return"<table>\n<thead>\n"+e+"</thead>\n"+(t=t&&"<tbody>"+t+"</tbody>")+"</table>\n"},e.prototype.tablerow=function(e){return"<tr>\n"+e+"</tr>\n"},e.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+"</"+n+">\n"},e.prototype.strong=function(e){return"<strong>"+e+"</strong>"},e.prototype.em=function(e){return"<em>"+e+"</em>"},e.prototype.codespan=function(e){return"<code>"+e+"</code>"},e.prototype.br=function(){return this.options.xhtml?"<br/>":"<br>"},e.prototype.del=function(e){return"<del>"+e+"</del>"},e.prototype.link=function(e,t,n){if(null===(e=en(this.options.sanitize,this.options.baseUrl,e)))return n;var r='<a href="'+tn(e)+'"';return t&&(r+=' title="'+t+'"'),r+=">"+n+"</a>"},e.prototype.image=function(e,t,n){if(null===(e=en(this.options.sanitize,this.options.baseUrl,e)))return n;var r='<img src="'+e+'" alt="'+n+'"';return t&&(r+=' title="'+t+'"'),r+=this.options.xhtml?"/>":">"},e.prototype.text=function(e){return e},e}(),rn=function(){function e(){}return e.prototype.strong=function(e){return e},e.prototype.em=function(e){return e},e.prototype.codespan=function(e){return e},e.prototype.del=function(e){return e},e.prototype.html=function(e){return e},e.prototype.text=function(e){return e},e.prototype.link=function(e,t,n){return""+n},e.prototype.image=function(e,t,n){return""+n},e.prototype.br=function(){return""},e}(),an=function(){function e(){this.seen={}}return e.prototype.slug=function(e){var t=e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t))for(var n=t;this.seen[n]++,t=n+"-"+this.seen[n],this.seen.hasOwnProperty(t););return this.seen[t]=0,t},e}(),on=st.defaults,sn=At,ln=function(){function n(e){this.options=e||on,this.options.renderer=this.options.renderer||new nn,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new rn,this.slugger=new an}return n.parse=function(e,t){return new n(t).parse(e)},n.prototype.parse=function(e,t){void 0===t&&(t=!0);var n,r,i,a,o,s,l,c,u,p,d,f,h,g,m,v,b,y,k="",x=e.length;for(n=0;n<x;n++)switch((p=e[n]).type){case"space":continue;case"hr":k+=this.renderer.hr();continue;case"heading":k+=this.renderer.heading(this.parseInline(p.tokens),p.depth,sn(this.parseInline(p.tokens,this.textRenderer)),this.slugger);continue;case"code":k+=this.renderer.code(p.text,p.lang,p.escaped);continue;case"table":for(l=c="",a=p.header.length,r=0;r<a;r++)l+=this.renderer.tablecell(this.parseInline(p.tokens.header[r]),{header:!0,align:p.align[r]});for(c+=this.renderer.tablerow(l),u="",a=p.cells.length,r=0;r<a;r++){for(l="",o=(s=p.tokens.cells[r]).length,i=0;i<o;i++)l+=this.renderer.tablecell(this.parseInline(s[i]),{header:!1,align:p.align[i]});u+=this.renderer.tablerow(l)}k+=this.renderer.table(c,u);continue;case"blockquote":u=this.parse(p.tokens),k+=this.renderer.blockquote(u);continue;case"list":for(d=p.ordered,f=p.start,h=p.loose,a=p.items.length,u="",r=0;r<a;r++)v=(m=p.items[r]).checked,b=m.task,g="",m.task&&(y=this.renderer.checkbox(v),h?0<m.tokens.length&&"text"===m.tokens[0].type?(m.tokens[0].text=y+" "+m.tokens[0].text,m.tokens[0].tokens&&0<m.tokens[0].tokens.length&&"text"===m.tokens[0].tokens[0].type&&(m.tokens[0].tokens[0].text=y+" "+m.tokens[0].tokens[0].text)):m.tokens.unshift({type:"text",text:y}):g+=y),g+=this.parse(m.tokens,h),u+=this.renderer.listitem(g,b,v);k+=this.renderer.list(u,d,f);continue;case"html":k+=this.renderer.html(p.text);continue;case"paragraph":k+=this.renderer.paragraph(this.parseInline(p.tokens));continue;case"text":for(u=p.tokens?this.parseInline(p.tokens):p.text;n+1<x&&"text"===e[n+1].type;)u+="\n"+((p=e[++n]).tokens?this.parseInline(p.tokens):p.text);k+=t?this.renderer.paragraph(u):u;continue;default:var w='Token with "'+p.type+'" type was not found.';if(this.options.silent)return void console.error(w);throw new Error(w)}return k},n.prototype.parseInline=function(e,t){t=t||this.renderer;var n,r,i="",a=e.length;for(n=0;n<a;n++)switch((r=e[n]).type){case"escape":i+=t.text(r.text);break;case"html":i+=t.html(r.text);break;case"link":i+=t.link(r.href,r.title,this.parseInline(r.tokens,t));break;case"image":i+=t.image(r.href,r.title,r.text);break;case"strong":i+=t.strong(this.parseInline(r.tokens,t));break;case"em":i+=t.em(this.parseInline(r.tokens,t));break;case"codespan":i+=t.codespan(r.text);break;case"br":i+=t.br();break;case"del":i+=t.del(this.parseInline(r.tokens,t));break;case"text":i+=t.text(r.text);break;default:var o='Token with "'+r.type+'" type was not found.';if(this.options.silent)return void console.error(o);throw new Error(o)}return i},n}(),cn=Rt,un=zt,pn=St,dn=st.getDefaults,fn=st.changeDefaults,hn=st.defaults;function gn(e,n,r){if(null==e)throw new Error("marked(): input parameter is undefined or null");if("string"!=typeof e)throw new Error("marked(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected");if("function"==typeof n&&(r=n,n=null),n=cn({},gn.defaults,n||{}),un(n),r){var i,a=n.highlight;try{i=Qt.lex(e,n)}catch(e){return r(e)}function o(t){var e;if(!t)try{e=ln.parse(i,n)}catch(e){t=e}return n.highlight=a,t?r(t):r(null,e)}if(!a||a.length<3)return o();if(delete n.highlight,!i.length)return o();var s=0;return gn.walkTokens(i,function(n){"code"===n.type&&(s++,setTimeout(function(){a(n.text,n.lang,function(e,t){if(e)return o(e);null!=t&&t!==n.text&&(n.text=t,n.escaped=!0),0===--s&&o()})},0))}),void(0===s&&o())}try{var t=Qt.lex(e,n);return n.walkTokens&&gn.walkTokens(t,n.walkTokens),ln.parse(t,n)}catch(e){if(e.message+="\nPlease report this to https://github.com/markedjs/marked.",n.silent)return"<p>An error occurred:</p><pre>"+pn(e.message+"",!0)+"</pre>";throw e}}gn.options=gn.setOptions=function(e){return cn(gn.defaults,e),fn(gn.defaults),gn},gn.getDefaults=dn,gn.defaults=hn,gn.use=function(a){var e=cn({},a);if(a.renderer){function t(r){var i=o[r];o[r]=function(){for(var e=[],t=arguments.length;t--;)e[t]=arguments[t];var n=a.renderer[r].apply(o,e);return!1===n&&(n=i.apply(o,e)),n}}var o=gn.defaults.renderer||new nn;for(var n in a.renderer)t(n);e.renderer=o}if(a.tokenizer){function r(e){var r=i[s];i[s]=function(){for(var e=[],t=arguments.length;t--;)e[t]=arguments[t];var n=a.tokenizer[s].apply(i,e);return!1===n&&(n=r.apply(i,e)),n}}var i=gn.defaults.tokenizer||new Ht;for(var s in a.tokenizer)r();e.tokenizer=i}if(a.walkTokens){var l=gn.defaults.walkTokens;e.walkTokens=function(e){a.walkTokens(e),l&&l(e)}}gn.setOptions(e)},gn.walkTokens=function(e,t){for(var n=0,r=e;n<r.length;n+=1){var i=r[n];switch(t(i),i.type){case"table":for(var a=0,o=i.tokens.header;a<o.length;a+=1){var s=o[a];gn.walkTokens(s,t)}for(var l=0,c=i.tokens.cells;l<c.length;l+=1)for(var u=0,p=c[l];u<p.length;u+=1){var d=p[u];gn.walkTokens(d,t)}break;case"list":gn.walkTokens(i.items,t);break;default:i.tokens&&gn.walkTokens(i.tokens,t)}}},gn.Parser=ln,gn.parser=ln.parse,gn.Renderer=nn,gn.TextRenderer=rn,gn.Lexer=Qt,gn.lexer=Qt.lex,gn.Tokenizer=Ht,gn.Slugger=an;var mn=gn.parse=gn;function vn(e,t){if(void 0===t&&(t='<ul class="app-sub-sidebar">{inner}</ul>'),!e||!e.length)return"";var n="";return e.forEach(function(e){n+='<li><a class="section-link" href="'+e.slug+'" title="'+e.title+'">'+e.title+"</a></li>",e.children&&(n+=vn(e.children,t))}),t.replace("{inner}",n)}function bn(e,t){return'<p class="'+e+'">'+t.slice(5).trim()+"</p>"}function yn(e,r){var i=[],a={};return e.forEach(function(e){var t=e.level||1,n=t-1;r<t||(a[n]?a[n].children=(a[n].children||[]).concat(e):i.push(e),a[t]=e)}),i}var kn={},xn=/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g;function wn(e){return e.toLowerCase()}function _n(e){if("string"!=typeof e)return"";var t=e.trim().replace(/[A-Z]+/g,wn).replace(/<[^>\d]+>/g,"").replace(xn,"").replace(/\s/g,"-").replace(/-+/g,"-").replace(/^(\d)/,"_$1"),n=kn[t];return n=l.call(kn,t)?n+1:0,(kn[t]=n)&&(t=t+"-"+n),t}function Sn(e,t){return'<img class="emoji" src="https://github.githubassets.com/images/icons/emoji/'+t+'.png" alt="'+t+'" />'}function An(e){void 0===e&&(e="");var r={};return{str:e=e&&e.replace(/^'/,"").replace(/'$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,t,n){return-1===t.indexOf(":")?(r[t]=n&&n.replace(/"/g,"")||!0,""):e}).trim(),config:r}}_n.clear=function(){kn={}};var Tn,En=at(function(e){var a=function(c){var u=/\blang(?:uage)?-([\w-]+)\b/i,t=0,z={manual:c.Prism&&c.Prism.manual,disableWorkerMessageHandler:c.Prism&&c.Prism.disableWorkerMessageHandler,util:{encode:function e(t){return t instanceof O?new O(t.type,e(t.content),t.alias):Array.isArray(t)?t.map(e):t.replace(/&/g,"&").replace(/</g,"<").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).slice(8,-1)},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function n(e,r){var i,t;switch(r=r||{},z.util.type(e)){case"Object":if(t=z.util.objId(e),r[t])return r[t];for(var a in i={},r[t]=i,e)e.hasOwnProperty(a)&&(i[a]=n(e[a],r));return i;case"Array":return t=z.util.objId(e),r[t]?r[t]:(i=[],r[t]=i,e.forEach(function(e,t){i[t]=n(e,r)}),i);default:return e}},getLanguage:function(e){for(;e&&!u.test(e.className);)e=e.parentElement;return e?(e.className.match(u)||[,"none"])[1].toLowerCase():"none"},currentScript:function(){if("undefined"==typeof document)return null;if("currentScript"in document)return document.currentScript;try{throw new Error}catch(e){var t=(/at [^(\r\n]*\((.*):.+:.+\)$/i.exec(e.stack)||[])[1];if(t){var n=document.getElementsByTagName("script");for(var r in n)if(n[r].src==t)return n[r]}return null}},isActive:function(e,t,n){for(var r="no-"+t;e;){var i=e.classList;if(i.contains(t))return!0;if(i.contains(r))return!1;e=e.parentElement}return!!n}},languages:{extend:function(e,t){var n=z.util.clone(z.languages[e]);for(var r in t)n[r]=t[r];return n},insertBefore:function(n,e,t,r){var i=(r=r||z.languages)[n],a={};for(var o in i)if(i.hasOwnProperty(o)){if(o==e)for(var s in t)t.hasOwnProperty(s)&&(a[s]=t[s]);t.hasOwnProperty(o)||(a[o]=i[o])}var l=r[n];return r[n]=a,z.languages.DFS(z.languages,function(e,t){t===l&&e!=n&&(this[e]=a)}),a},DFS:function e(t,n,r,i){i=i||{};var a=z.util.objId;for(var o in t)if(t.hasOwnProperty(o)){n.call(t,o,t[o],r||o);var s=t[o],l=z.util.type(s);"Object"!==l||i[a(s)]?"Array"!==l||i[a(s)]||(i[a(s)]=!0,e(s,n,o,i)):(i[a(s)]=!0,e(s,n,null,i))}}},plugins:{},highlightAll:function(e,t){z.highlightAllUnder(document,e,t)},highlightAllUnder:function(e,t,n){var r={callback:n,container:e,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};z.hooks.run("before-highlightall",r),r.elements=Array.prototype.slice.apply(r.container.querySelectorAll(r.selector)),z.hooks.run("before-all-elements-highlight",r);for(var i,a=0;i=r.elements[a++];)z.highlightElement(i,!0===t,r.callback)},highlightElement:function(e,t,n){var r=z.util.getLanguage(e),i=z.languages[r];e.className=e.className.replace(u,"").replace(/\s+/g," ")+" language-"+r;var a=e.parentElement;a&&"pre"===a.nodeName.toLowerCase()&&(a.className=a.className.replace(u,"").replace(/\s+/g," ")+" language-"+r);var o={element:e,language:r,grammar:i,code:e.textContent};function s(e){o.highlightedCode=e,z.hooks.run("before-insert",o),o.element.innerHTML=o.highlightedCode,z.hooks.run("after-highlight",o),z.hooks.run("complete",o),n&&n.call(o.element)}if(z.hooks.run("before-sanity-check",o),!o.code)return z.hooks.run("complete",o),void(n&&n.call(o.element));if(z.hooks.run("before-highlight",o),o.grammar)if(t&&c.Worker){var l=new Worker(z.filename);l.onmessage=function(e){s(e.data)},l.postMessage(JSON.stringify({language:o.language,code:o.code,immediateClose:!0}))}else s(z.highlight(o.code,o.grammar,o.language));else s(z.util.encode(o.code))},highlight:function(e,t,n){var r={code:e,grammar:t,language:n};return z.hooks.run("before-tokenize",r),r.tokens=z.tokenize(r.code,r.grammar),z.hooks.run("after-tokenize",r),O.stringify(z.util.encode(r.tokens),r.language)},tokenize:function(e,t){var n=t.rest;if(n){for(var r in n)t[r]=n[r];delete t.rest}var i=new a;return M(i,i.head,e),function e(t,n,r,i,a,o){for(var s in r)if(r.hasOwnProperty(s)&&r[s]){var l=r[s];l=Array.isArray(l)?l:[l];for(var c=0;c<l.length;++c){if(o&&o.cause==s+","+c)return;var u=l[c],p=u.inside,d=!!u.lookbehind,f=!!u.greedy,h=0,g=u.alias;if(f&&!u.pattern.global){var m=u.pattern.toString().match(/[imsuy]*$/)[0];u.pattern=RegExp(u.pattern.source,m+"g")}for(var v=u.pattern||u,b=i.next,y=a;b!==n.tail&&!(o&&y>=o.reach);y+=b.value.length,b=b.next){var k=b.value;if(n.length>t.length)return;if(!(k instanceof O)){var x=1;if(f&&b!=n.tail.prev){v.lastIndex=y;var w=v.exec(t);if(!w)break;var _=w.index+(d&&w[1]?w[1].length:0),S=w.index+w[0].length,A=y;for(A+=b.value.length;A<=_;)b=b.next,A+=b.value.length;if(A-=b.value.length,y=A,b.value instanceof O)continue;for(var T=b;T!==n.tail&&(A<S||"string"==typeof T.value);T=T.next)x++,A+=T.value.length;x--,k=t.slice(y,A),w.index-=y}else{v.lastIndex=0;var w=v.exec(k)}if(w){d&&(h=w[1]?w[1].length:0);var _=w.index+h,E=w[0].slice(h),S=_+E.length,L=k.slice(0,_),R=k.slice(S),C=y+k.length;o&&C>o.reach&&(o.reach=C);var $=b.prev;L&&($=M(n,$,L),y+=L.length),N(n,$,x);var F=new O(s,p?z.tokenize(E,p):E,g,E);b=M(n,$,F),R&&M(n,b,R),1<x&&e(t,n,r,b.prev,y,{cause:s+","+c,reach:C})}}}}}}(e,i,t,i.head,0),function(e){var t=[],n=e.head.next;for(;n!==e.tail;)t.push(n.value),n=n.next;return t}(i)},hooks:{all:{},add:function(e,t){var n=z.hooks.all;n[e]=n[e]||[],n[e].push(t)},run:function(e,t){var n=z.hooks.all[e];if(n&&n.length)for(var r,i=0;r=n[i++];)r(t)}},Token:O};function O(e,t,n,r){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length}function a(){var e={value:null,prev:null,next:null},t={value:null,prev:e,next:null};e.next=t,this.head=e,this.tail=t,this.length=0}function M(e,t,n){var r=t.next,i={value:n,prev:t,next:r};return t.next=i,r.prev=i,e.length++,i}function N(e,t,n){for(var r=t.next,i=0;i<n&&r!==e.tail;i++)r=r.next;(t.next=r).prev=t,e.length-=i}if(c.Prism=z,O.stringify=function t(e,n){if("string"==typeof e)return e;if(Array.isArray(e)){var r="";return e.forEach(function(e){r+=t(e,n)}),r}var i={type:e.type,content:t(e.content,n),tag:"span",classes:["token",e.type],attributes:{},language:n},a=e.alias;a&&(Array.isArray(a)?Array.prototype.push.apply(i.classes,a):i.classes.push(a)),z.hooks.run("wrap",i);var o="";for(var s in i.attributes)o+=" "+s+'="'+(i.attributes[s]||"").replace(/"/g,""")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+o+">"+i.content+"</"+i.tag+">"},!c.document)return c.addEventListener&&(z.disableWorkerMessageHandler||c.addEventListener("message",function(e){var t=JSON.parse(e.data),n=t.language,r=t.code,i=t.immediateClose;c.postMessage(z.highlight(r,z.languages[n],n)),i&&c.close()},!1)),z;var e=z.util.currentScript();function n(){z.manual||z.highlightAll()}if(e&&(z.filename=e.src,e.hasAttribute("data-manual")&&(z.manual=!0)),!z.manual){var r=document.readyState;"loading"===r||"interactive"===r&&e&&e.defer?document.addEventListener("DOMContentLoaded",n):window.requestAnimationFrame?window.requestAnimationFrame(n):window.setTimeout(n,16)}return z}("undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{});e.exports&&(e.exports=a),void 0!==it&&(it.Prism=a),a.languages.markup={comment:/<!--[\s\S]*?-->/,prolog:/<\?[\s\S]+?\?>/,doctype:{pattern:/<!DOCTYPE(?:[^>"'[\]]|"[^"]*"|'[^']*')+(?:\[(?:[^<"'\]]|"[^"]*"|'[^']*'|<(?!!--)|<!--(?:[^-]|-(?!->))*-->)*\]\s*)?>/i,greedy:!0,inside:{"internal-subset":{pattern:/(\[)[\s\S]+(?=\]>$)/,lookbehind:!0,greedy:!0,inside:null},string:{pattern:/"[^"]*"|'[^']*'/,greedy:!0},punctuation:/^<!|>$|[[\]]/,"doctype-tag":/^DOCTYPE/,name:/[^\s<>'"]+/}},cdata:/<!\[CDATA\[[\s\S]*?]]>/i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/,inside:{punctuation:[{pattern:/^=/,alias:"attr-equals"},/"|'/]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:[{pattern:/&[\da-z]{1,8};/i,alias:"named-entity"},/&#x?[\da-f]{1,8};/i]},a.languages.markup.tag.inside["attr-value"].inside.entity=a.languages.markup.entity,a.languages.markup.doctype.inside["internal-subset"].inside=a.languages.markup,a.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Object.defineProperty(a.languages.markup.tag,"addInlined",{value:function(e,t){var n={};n["language-"+t]={pattern:/(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,lookbehind:!0,inside:a.languages[t]},n.cdata=/^<!\[CDATA\[|\]\]>$/i;var r={"included-cdata":{pattern:/<!\[CDATA\[[\s\S]*?\]\]>/i,inside:n}};r["language-"+t]={pattern:/[\s\S]+/,inside:a.languages[t]};var i={};i[e]={pattern:RegExp(/(<__[\s\S]*?>)(?:<!\[CDATA\[(?:[^\]]|\](?!\]>))*\]\]>|(?!<!\[CDATA\[)[\s\S])*?(?=<\/__>)/.source.replace(/__/g,function(){return e}),"i"),lookbehind:!0,greedy:!0,inside:r},a.languages.insertBefore("markup","cdata",i)}}),a.languages.html=a.languages.markup,a.languages.mathml=a.languages.markup,a.languages.svg=a.languages.markup,a.languages.xml=a.languages.extend("markup",{}),a.languages.ssml=a.languages.xml,a.languages.atom=a.languages.xml,a.languages.rss=a.languages.xml,function(e){var t=/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;e.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,inside:{rule:/^@[\w-]+/,"selector-function-argument":{pattern:/(\bselector\s*\((?!\s*\))\s*)(?:[^()]|\((?:[^()]|\([^()]*\))*\))+?(?=\s*\))/,lookbehind:!0,alias:"selector"},keyword:{pattern:/(^|[^\w-])(?:and|not|only|or)(?![\w-])/,lookbehind:!0}}},url:{pattern:RegExp("\\burl\\((?:"+t.source+"|"+/(?:[^\\\r\n()"']|\\[\s\S])*/.source+")\\)","i"),greedy:!0,inside:{function:/^url/i,punctuation:/^\(|\)$/,string:{pattern:RegExp("^"+t.source+"$"),alias:"url"}}},selector:RegExp("[^{}\\s](?:[^{};\"']|"+t.source+")*?(?=\\s*\\{)"),string:{pattern:t,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/!important\b/i,function:/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:,]/},e.languages.css.atrule.inside.rest=e.languages.css;var n=e.languages.markup;n&&(n.tag.addInlined("style","css"),e.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:n.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:e.languages.css}},alias:"language-css"}},n.tag))}(a),a.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/(\b(?:class|interface|extends|implements|trait|instanceof|new)\s+|\bcatch\s+\()[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,boolean:/\b(?:true|false)\b/,function:/\w+(?=\()/,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/[<>]=?|[!=]=?=?|--?|\+\+?|&&?|\|\|?|[?*/~^%]/,punctuation:/[{}[\];(),.:]/},a.languages.javascript=a.languages.extend("clike",{"class-name":[a.languages.clike["class-name"],{pattern:/(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,lookbehind:!0}],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},{pattern:/(^|[^.]|\.\.\.\s*)\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|(?:get|set)(?=\s*[\[$\w\xA0-\uFFFF])|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)\b/,lookbehind:!0}],number:/\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,function:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,operator:/--|\+\+|\*\*=?|=>|&&=?|\|\|=?|[!=]==|<<=?|>>>?=?|[-+*/%&|^!=<>]=?|\.{3}|\?\?=?|\?\.?|[~:]/}),a.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,a.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s]|\b(?:return|yield))\s*)\/(?:\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/)*(?:$|[\r\n,.;:})\]]|\/\/))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,alias:"function"},parameter:[{pattern:/(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,lookbehind:!0,inside:a.languages.javascript},{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,inside:a.languages.javascript},{pattern:/(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,lookbehind:!0,inside:a.languages.javascript},{pattern:/((?:\b|\s|^)(?!(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*|\]\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,lookbehind:!0,inside:a.languages.javascript}],constant:/\b[A-Z](?:[A-Z_]|\dx?)*\b/}),a.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,greedy:!0,inside:{"template-punctuation":{pattern:/^`|`$/,alias:"string"},interpolation:{pattern:/((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,lookbehind:!0,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:a.languages.javascript}},string:/[\s\S]+/}}}),a.languages.markup&&a.languages.markup.tag.addInlined("script","javascript"),a.languages.js=a.languages.javascript,function(){if("undefined"!=typeof self&&self.Prism&&self.document){var l=window.Prism,c={js:"javascript",py:"python",rb:"ruby",ps1:"powershell",psm1:"powershell",sh:"bash",bat:"batch",h:"c",tex:"latex"},u="data-src-status",p="loading",d="pre[data-src]:not(["+u+'="loaded"]):not(['+u+'="'+p+'"])',r=/\blang(?:uage)?-([\w-]+)\b/i;l.hooks.add("before-highlightall",function(e){e.selector+=", "+d}),l.hooks.add("before-sanity-check",function(e){var t=e.element;if(t.matches(d)){e.code="",t.setAttribute(u,p);var n=t.appendChild(document.createElement("CODE"));n.textContent="Loading…";var r=t.getAttribute("data-src"),i=e.language;if("none"===i){var a=(/\.(\w+)$/.exec(r)||[,"none"])[1];i=c[a]||a}f(n,i),f(t,i);var o=l.plugins.autoloader;o&&o.loadLanguages(i);var s=new XMLHttpRequest;s.open("GET",r,!0),s.onreadystatechange=function(){4==s.readyState&&(s.status<400&&s.responseText?(t.setAttribute(u,"loaded"),n.textContent=s.responseText,l.highlightElement(n)):(t.setAttribute(u,"failed"),400<=s.status?n.textContent=function(e,t){return"✖ Error "+e+" while fetching file: "+t}(s.status,s.statusText):n.textContent="✖ Error: File does not exist or is empty"))},s.send(null)}});var e=!(l.plugins.fileHighlight={highlight:function(e){for(var t,n=(e||document).querySelectorAll(d),r=0;t=n[r++];)l.highlightElement(t)}});l.fileHighlight=function(){e||(console.warn("Prism.fileHighlight is deprecated. Use `Prism.plugins.fileHighlight.highlight` instead."),e=!0),l.plugins.fileHighlight.highlight.apply(this,arguments)}}function f(e,t){var n=e.className;n=n.replace(r," ")+" language-"+t,e.className=n.replace(/\s+/g," ").trim()}}()});function Ln(e,t){return"___"+e.toUpperCase()+t+"___"}Tn=Prism,Object.defineProperties(Tn.languages["markup-templating"]={},{buildPlaceholders:{value:function(r,i,e,a){if(r.language===i){var o=r.tokenStack=[];r.code=r.code.replace(e,function(e){if("function"==typeof a&&!a(e))return e;for(var t,n=o.length;-1!==r.code.indexOf(t=Ln(i,n));)++n;return o[n]=e,t}),r.grammar=Tn.languages.markup}}},tokenizePlaceholders:{value:function(f,h){if(f.language===h&&f.tokenStack){f.grammar=Tn.languages[h];var g=0,m=Object.keys(f.tokenStack);!function e(t){for(var n=0;n<t.length&&!(g>=m.length);n++){var r=t[n];if("string"==typeof r||r.content&&"string"==typeof r.content){var i=m[g],a=f.tokenStack[i],o="string"==typeof r?r:r.content,s=Ln(h,i),l=o.indexOf(s);if(-1<l){++g;var c=o.substring(0,l),u=new Tn.Token(h,Tn.tokenize(a,f.grammar),"language-"+h,a),p=o.substring(l+s.length),d=[];c&&d.push.apply(d,e([c])),d.push(u),p&&d.push.apply(d,e([p])),"string"==typeof r?t.splice.apply(t,[n,1].concat(d)):r.content=d}}else r.content&&e(r.content)}return t}(f.tokens)}}}});var Rn={},Cn={markdown:function(e){return{url:e}},mermaid:function(e){return{url:e}},iframe:function(e,t){return{html:'<iframe src="'+e+'" '+(t||"width=100% height=400")+"></iframe>"}},video:function(e,t){return{html:'<video src="'+e+'" '+(t||"controls")+">Not Support</video>"}},audio:function(e,t){return{html:'<audio src="'+e+'" '+(t||"controls")+">Not Support</audio>"}},code:function(e,t){var n=e.match(/\.(\w+)$/);return"md"===(n=t||n&&n[1])&&(n="markdown"),{url:e,lang:n}}},$n=function(i,e){var a=this;this.config=i,this.router=e,this.cacheTree={},this.toc=[],this.cacheTOC={},this.linkTarget=i.externalLinkTarget||"_blank",this.linkRel="_blank"===this.linkTarget?i.externalLinkRel||"noopener":"",this.contentBase=e.getBasePath();var o,t=this._initRenderer();this.heading=t.heading;var n=i.markdown||{};o=r(n)?n(mn,t):(mn.setOptions(f(n,{renderer:f(t,n.renderer)})),mn),this._marked=o,this.compile=function(n){var r=!0,e=s(function(e){r=!1;var t="";return n?(t=c(n)?o(n):o.parser(n),t=i.noEmoji?t:function(e){return e.replace(/:\+1:/g,":thumbsup:").replace(/:-1:/g,":thumbsdown:").replace(/<(pre|template|code)[^>]*?>[\s\S]+?<\/(pre|template|code)>/g,function(e){return e.replace(/:/g,"__colon__")}).replace(/:(\w+?):/gi,window.emojify||Sn).replace(/__colon__/g,":")}(t),_n.clear(),t):n})(n),t=a.router.parse().file;return r?a.toc=a.cacheTOC[t]:a.cacheTOC[t]=[].concat(a.toc),e}};$n.prototype.compileEmbed=function(e,t){var n,r=An(t),i=r.str,a=r.config;if(t=i,a.include){var o;if(Q(e)||(e=re(this.contentBase,ee(this.router.getCurrentPath()),e)),a.type&&(o=Cn[a.type]))(n=o.call(this,e,t)).type=a.type;else{var s="code";/\.(md|markdown)/.test(e)?s="markdown":/\.mmd/.test(e)?s="mermaid":/\.html?/.test(e)?s="iframe":/\.(mp4|ogg)/.test(e)?s="video":/\.mp3/.test(e)&&(s="audio"),(n=Cn[s].call(this,e,t)).type=s}return n.fragment=a.fragment,n}},$n.prototype._matchNotCompileLink=function(e){for(var t=this.config.noCompileLinks||[],n=0;n<t.length;n++){var r=t[n];if((Rn[r]||(Rn[r]=new RegExp("^"+r+"$"))).test(e))return e}},$n.prototype._initRenderer=function(){var e=new mn.Renderer,t=this.linkTarget,n=this.linkRel,l=this.router,r=this.contentBase,c=this,i={};return i.heading=e.heading=function(e,t){var n=An(e),r=n.str,i=n.config,a={level:t,title:r};/<!-- {docsify-ignore} -->/g.test(r)&&(r=r.replace("\x3c!-- {docsify-ignore} --\x3e",""),a.title=r,a.ignoreSubHeading=!0),/{docsify-ignore}/g.test(r)&&(r=r.replace("{docsify-ignore}",""),a.title=r,a.ignoreSubHeading=!0),/<!-- {docsify-ignore-all} -->/g.test(r)&&(r=r.replace("\x3c!-- {docsify-ignore-all} --\x3e",""),a.title=r,a.ignoreAllSubs=!0),/{docsify-ignore-all}/g.test(r)&&(r=r.replace("{docsify-ignore-all}",""),a.title=r,a.ignoreAllSubs=!0);var o=_n(i.id||r),s=l.toURL(l.getCurrentPath(),{id:o});return a.slug=s,c.toc.push(a),"<h"+t+' id="'+o+'"><a href="'+s+'" data-id="'+o+'" class="anchor"><span>'+r+"</span></a></h"+t+">"},i.code=function(e){return e.renderer.code=function(e,t){void 0===t&&(t="");var n=En.languages[t]||En.languages.markup;return'<pre v-pre data-lang="'+t+'"><code class="lang-'+t+'">'+En.highlight(e.replace(/@DOCSIFY_QM@/g,"`"),n)+"</code></pre>"}}({renderer:e}),i.link=function(e){var t=e.renderer,s=e.router,l=e.linkTarget,c=e.linkRel,u=e.compilerClass;return t.link=function(e,t,n){void 0===t&&(t="");var r=[],i=An(t),a=i.str,o=i.config;return l=o.target||l,c="_blank"===l?u.config.externalLinkRel||"noopener":"",t=a,Q(e)||u._matchNotCompileLink(e)||o.ignore?(!Q(e)&&e.startsWith("./")&&(e=document.URL.replace(/\/(?!.*\/).*/,"/").replace("#/./","")+e),r.push(0===e.indexOf("mailto:")?"":'target="'+l+'"'),r.push(0===e.indexOf("mailto:")?"":""!==c?' rel="'+c+'"':"")):(e===u.config.homepage&&(e="README"),e=s.toURL(e,null,s.getCurrentPath())),o.crossorgin&&"_self"===l&&"history"===u.config.routerMode&&-1===u.config.crossOriginLinks.indexOf(e)&&u.config.crossOriginLinks.push(e),o.disabled&&(r.push("disabled"),e="javascript:void(0)"),o.class&&r.push('class="'+o.class+'"'),o.id&&r.push('id="'+o.id+'"'),t&&r.push('title="'+t+'"'),'<a href="'+e+'" '+r.join(" ")+">"+n+"</a>"}}({renderer:e,router:l,linkTarget:t,linkRel:n,compilerClass:c}),i.paragraph=function(e){return e.renderer.paragraph=function(e){return/^!>/.test(e)?bn("tip",e):/^\?>/.test(e)?bn("warn",e):"<p>"+e+"</p>"}}({renderer:e}),i.image=function(e){var t=e.renderer,p=e.contentBase,d=e.router;return t.image=function(e,t,n){var r=e,i=[],a=An(t),o=a.str,s=a.config;if(t=o,s["no-zoom"]&&i.push("data-no-zoom"),t&&i.push('title="'+t+'"'),s.size){var l=s.size.split("x"),c=l[0],u=l[1];u?i.push('width="'+c+'" height="'+u+'"'):i.push('width="'+c+'"')}return s.class&&i.push('class="'+s.class+'"'),s.id&&i.push('id="'+s.id+'"'),Q(e)||(r=re(p,ee(d.getCurrentPath()),e)),0<i.length?'<img src="'+r+'" data-origin="'+e+'" alt="'+n+'" '+i.join(" ")+" />":'<img src="'+r+'" data-origin="'+e+'" alt="'+n+'"'+i+">"}}({renderer:e,contentBase:r,router:l}),i.list=function(e){return e.renderer.list=function(e,t,n){var r=t?"ol":"ul";return"<"+r+" "+[/<li class="task-list-item">/.test(e.split('class="task-list"')[0])?'class="task-list"':"",n&&1<n?'start="'+n+'"':""].join(" ").trim()+">"+e+"</"+r+">"}}({renderer:e}),i.listitem=function(e){return e.renderer.listitem=function(e){return/^(<input.*type="checkbox"[^>]*>)/.test(e)?'<li class="task-list-item"><label>'+e+"</label></li>":"<li>"+e+"</li>"}}({renderer:e}),e.origin=i,e},$n.prototype.sidebar=function(e,t){var n=this.toc,r=this.router.getCurrentPath(),i="";if(e)i=this.compile(e);else{for(var a=0;a<n.length;a++)if(n[a].ignoreSubHeading){var o=n[a].level;n.splice(a,1);for(var s=a;o<n[s].level&&s<n.length;s++)n.splice(s,1)&&s--&&a++;a--}var l=this.cacheTree[r]||yn(n,t);i=vn(l,"<ul>{inner}</ul>"),this.cacheTree[r]=l}return i},$n.prototype.subSidebar=function(e){if(e){var t=this.router.getCurrentPath(),n=this.cacheTree,r=this.toc;r[0]&&r[0].ignoreAllSubs&&r.splice(0),r[0]&&1===r[0].level&&r.shift();for(var i=0;i<r.length;i++)r[i].ignoreSubHeading&&r.splice(i,1)&&i--;var a=n[t]||yn(r,e);return n[t]=a,this.toc=[],vn(a)}this.toc=[]},$n.prototype.header=function(e,t){return this.heading(e,t)},$n.prototype.article=function(e){return this.compile(e)},$n.prototype.cover=function(e){var t=this.toc.slice(),n=this.compile(e);return this.toc=t.slice(),n};var Fn=function(e){var t=function(e){var t=e.match(/^[ \t]*(?=\S)/gm);return t?Math.min.apply(Math,t.map(function(e){return e.length})):0}(e);if(0===t)return e;var n=new RegExp("^[ \\t]{"+t+"}","gm");return e.replace(n,"")},zn={};function On(e,i){var o=e.compiler,a=e.raw;void 0===a&&(a="");var t=e.fetch,n=zn[a];if(n){var r=n.slice();return r.links=n.links,i(r)}var s=o._marked,l=s.lexer(a),c=[],u=s.Lexer.rules.inline.link,p=l.links;l.forEach(function(e,a){"paragraph"===e.type&&(e.text=e.text.replace(new RegExp(u.source,"g"),function(e,t,n,r){var i=o.compileEmbed(n,r);return i&&c.push({index:a,embed:i}),e}))});var d=[];!function(e,o){var t,n=e.embedTokens,s=e.compile,l=(e.fetch,0),c=1;if(!n.length)return o({});for(;t=n[l++];){var r=function(a){return function(e){var t;if(e)if("markdown"===a.embed.type){var n=a.embed.url.split("/");n.pop(),n=n.join("/"),e=e.replace(/\[([^[\]]+)\]\(([^)]+)\)/g,function(e){var t=e.indexOf("(");return e.substring(t).startsWith("(.")?e.substring(0,t)+"("+window.location.protocol+"//"+window.location.host+n+"/"+e.substring(t+1,e.length-1)+")":e}),!0===(($docsify.frontMatter||{}).installed||!1)&&(e=$docsify.frontMatter.parseMarkdown(e)),t=s.lexer(e)}else if("code"===a.embed.type){if(a.embed.fragment){var r=a.embed.fragment,i=new RegExp("(?:###|\\/\\/\\/)\\s*\\["+r+"\\]([\\s\\S]*)(?:###|\\/\\/\\/)\\s*\\["+r+"\\]");e=Fn((e.match(i)||[])[1]||"").trim()}t=s.lexer("```"+a.embed.lang+"\n"+e.replace(/`/g,"@DOCSIFY_QM@")+"\n```\n")}else"mermaid"===a.embed.type?(t=[{type:"html",text:'<div class="mermaid">\n'+e+"\n</div>"}]).links={}:(t=[{type:"html",text:e}]).links={};o({token:a,embedToken:t}),++c>=l&&o({})}}(t);t.embed.url?q(t.embed.url).then(r):r(t.embed.html)}}({compile:s,embedTokens:c,fetch:t},function(e){var t=e.embedToken,n=e.token;if(n){var r=n.index;d.forEach(function(e){r>e.start&&(r+=e.length)}),f(p,t.links),l=l.slice(0,r).concat(t,l.slice(r+1)),d.push({start:r,length:t.length-1})}else zn[a]=l.concat(),l.links=zn[a].links=p,i(l)})}function Mn(){var e=y(".markdown-section>script").filter(function(e){return!/template/.test(e.type)})[0];if(!e)return!1;var t=e.innerText.trim();if(!t)return!1;setTimeout(function(e){window.__EXECUTE_RESULT__=new Function(t)()},0)}function Nn(e,t,n){return t="function"==typeof n?n(t):"string"==typeof n?function(r,i){var a=[],o=0;return r.replace(T,function(t,e,n){a.push(r.substring(o,n-1)),o=n+=t.length+1,a.push(i&&i[t]||function(e){return("00"+("string"==typeof E[t]?e[E[t]]():E[t](e))).slice(-t.length)})}),o!==r.length&&a.push(r.substring(o)),function(e){for(var t="",n=0,r=e||new Date;n<a.length;n++)t+="string"==typeof a[n]?a[n]:a[n](r);return t}}(n)(new Date(t)):t,e.replace(/{docsify-updated}/g,t)}function Pn(e){e=e||"<h1>404 - Not found</h1>",this._renderTo(".markdown-section",e),this.config.loadSidebar||this._renderSidebar(),!1===this.config.executeScript||void 0===window.Vue||Mn()?this.config.executeScript&&Mn():setTimeout(function(e){var t=window.__EXECUTE_RESULT__;t&&t.$destroy&&t.$destroy(),window.__EXECUTE_RESULT__=(new window.Vue).$mount("#main")},0)}function Dn(e){var t=e.config;e.compiler=new $n(t,e.router),window.__current_docsify_compiler__=e.compiler;var n=t.el||"#app",r=b("nav")||g("nav"),i=b(n),a="",o=v;if(i){if(t.repo&&(a+=function(e,t){return e?(/\/\//.test(e)||(e="https://github.com/"+e),'<a href="'+(e=e.replace(/^git\+/,""))+'" target="'+(t=t||"_blank")+'" class="github-corner" aria-label="View source on Github"><svg viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>'):""}(t.repo,t.cornerExternalLinkTarge)),t.coverpage&&(a+=function(){var e=", 100%, 85%";return'<section class="cover show" style="background: '+("linear-gradient(to left bottom, hsl("+Math.floor(255*Math.random())+e+") 0%,hsl("+Math.floor(255*Math.random())+e+") 100%)")+'"><div class="mask"></div><div class="cover-main">\x3c!--cover--\x3e</div></section>'}()),t.logo){var s=/^data:image/.test(t.logo),l=/(?:http[s]?:)?\/\//.test(t.logo),c=/^\./.test(t.logo);s||l||c||(t.logo=re(e.router.getBasePath(),t.logo))}a+=function(e){var t=e.name?e.name:"";return"<main>"+('<button class="sidebar-toggle" aria-label="Menu"><div class="sidebar-toggle-button"><span></span><span></span><span></span></div></button><aside class="sidebar">'+(e.name?'<h1 class="app-name"><a class="app-name-link" data-nosearch>'+(e.logo?'<img alt="'+t+'" src='+e.logo+">":t)+"</a></h1>":"")+'<div class="sidebar-nav">\x3c!--sidebar--\x3e</div></aside>')+'<section class="content"><article class="markdown-section" id="main">\x3c!--main--\x3e</article></section></main>'}(t),e._renderTo(i,a,!0)}else e.rendered=!0;t.mergeNavbar&&h?o=b(".sidebar"):(r.classList.add("app-nav"),t.repo||r.classList.add("no-badge")),t.loadNavbar&&k(o,r),t.themeColor&&(u.head.appendChild(g("div",function(e){return"<style>:root{--theme-color: "+e+";}</style>"}(t.themeColor)).firstElementChild),function(n){if(!(window.CSS&&window.CSS.supports&&window.CSS.supports("(--v:red)"))){var e=y("style:not(.inserted),link");[].forEach.call(e,function(e){if("STYLE"===e.nodeName)U(e,n);else if("LINK"===e.nodeName){var t=e.getAttribute("href");if(!/\.css$/.test(t))return;q(t).then(function(e){var t=g("style",e);d.appendChild(t),U(t,n)})}})}}(t.themeColor)),e._updateRender(),_(v,"ready")}var jn={};function Hn(e){this.config=e}function In(e){var t=location.href.indexOf("#");location.replace(location.href.slice(0,0<=t?t:0)+"#"+e)}Hn.prototype.getBasePath=function(){return this.config.basePath},Hn.prototype.getFile=function(e,t){void 0===e&&(e=this.getCurrentPath());var n=this.config,r=this.getBasePath(),i="string"==typeof n.ext?n.ext:".md";return e=(e=function(e,t){return new RegExp("\\.("+t.replace(/^\./,"")+"|html)$","g").test(e)?e:/\/$/g.test(e)?e+"README"+t:""+e+t}(e=n.alias?function e(t,n,r){var i=Object.keys(n).filter(function(e){return(jn[e]||(jn[e]=new RegExp("^"+e+"$"))).test(t)&&t!==r})[0];return i?e(t.replace(jn[i],n[i]),n,t):t}(e,n.alias):e,i))==="/README"+i&&n.homepage||e,e=Q(e)?e:re(r,e),t&&(e=e.replace(new RegExp("^"+r),"")),e},Hn.prototype.onchange=function(e){void 0===e&&(e=p),e()},Hn.prototype.getCurrentPath=function(){},Hn.prototype.normalize=function(){},Hn.prototype.parse=function(){},Hn.prototype.toURL=function(e,t,n){var r=n&&"#"===e[0],i=this.parse(ie(e));if(i.query=f({},i.query,t),e=(e=i.path+K(i.query)).replace(/\.md(\?)|\.md$/,"$1"),r){var a=n.indexOf("?");e=(0<a?n.substring(0,a):n)+e}if(this.config.relativePath&&0!==e.indexOf("/")){var o=n.substring(0,n.lastIndexOf("/")+1);return te(ne(o+e))}return te("/"+e)};var qn=function(r){function e(e){r.call(this,e),this.mode="hash"}return r&&(e.__proto__=r),((e.prototype=Object.create(r&&r.prototype)).constructor=e).prototype.getBasePath=function(){var e=window.location.pathname||"",t=this.config.basePath;return/^(\/|https?:)/g.test(t)?t:te(e+"/"+t)},e.prototype.getCurrentPath=function(){var e=location.href,t=e.indexOf("#");return-1===t?"":e.slice(t+1)},e.prototype.onchange=function(n){void 0===n&&(n=p);var r=!1;x("click",function(e){var t="A"===e.target.tagName?e.target:e.target.parentNode;t&&"A"===t.tagName&&!/_blank/.test(t.target)&&(r=!0)}),x("hashchange",function(e){var t=r?"navigate":"history";r=!1,n({event:e,source:t})})},e.prototype.normalize=function(){var e=this.getCurrentPath();if("/"===(e=ie(e)).charAt(0))return In(e);In("/"+e)},e.prototype.parse=function(e){void 0===e&&(e=location.href);var t="",n=e.indexOf("#");0<=n&&(e=e.slice(n+1));var r=e.indexOf("?");return 0<=r&&(t=e.slice(r+1),e=e.slice(0,r)),{path:e,file:this.getFile(e,!0),query:X(t)}},e.prototype.toURL=function(e,t,n){return"#"+r.prototype.toURL.call(this,e,t,n)},e}(Hn),Un=function(t){function e(e){t.call(this,e),this.mode="history"}return t&&(e.__proto__=t),((e.prototype=Object.create(t&&t.prototype)).constructor=e).prototype.getCurrentPath=function(){var e=this.getBasePath(),t=window.location.pathname;return e&&0===t.indexOf(e)&&(t=t.slice(e.length)),(t||"/")+window.location.search+window.location.hash},e.prototype.onchange=function(r){var i=this;void 0===r&&(r=p),x("click",function(e){var t="A"===e.target.tagName?e.target:e.target.parentNode;if("A"===t.tagName&&!/_blank/.test(t.target)){e.preventDefault();var n=t.href;-1!==i.config.crossOriginLinks.indexOf(n)?window.open(n,"_self"):window.history.pushState({key:n},"",n),r({event:e,source:"navigate"})}}),x("popstate",function(e){r({event:e,source:"history"})})},e.prototype.parse=function(e){void 0===e&&(e=location.href);var t="",n=e.indexOf("?");0<=n&&(t=e.slice(n+1),e=e.slice(0,n));var r=re(location.origin),i=e.indexOf(r);return-1<i&&(e=e.slice(i+r.length)),{path:e,file:this.getFile(e),query:X(t)}},e}(Hn);var Bn={};function Zn(e){e.router.normalize(),e.route=e.router.parse(),v.setAttribute("data-page",e.route.file)}function Wn(e){!function(e){function t(e){return v.classList.toggle("close")}null!=(e=m(e))&&(x(e,"click",function(e){e.stopPropagation(),t()}),h&&x(v,"click",function(e){return v.classList.contains("close")&&t()}))}("button.sidebar-toggle",e.router),function(e){null!=(e=m(e))&&x(e,"click",function(e){var t=e.target;"A"===t.nodeName&&t.nextSibling&&t.nextSibling.classList&&t.nextSibling.classList.contains("app-sub-sidebar")&&_(t.parentNode,"collapse")})}(".sidebar",e.router),e.config.coverpage?h||x("scroll",W):v.classList.add("sticky")}function Yn(t,n,r,i,a,e){t=e?t:t.replace(/\/$/,""),(t=ee(t))&&q(a.router.getFile(t+r)+n,!1,a.config.requestHeaders).then(i,function(e){return Yn(t,n,r,i,a)})}var Gn=Object.freeze({__proto__:null,cached:s,hyphenate:a,hasOwn:l,merge:f,isPrimitive:c,noop:p,isFn:r,inBrowser:!0,isMobile:h,supportsPushState:i,parseQuery:X,stringifyQuery:K,isAbsolutePath:Q,removeParams:J,getParentPath:ee,cleanPath:te,resolvePath:ne,getPath:re,replaceSlug:ie});function Vn(){this._init()}var Xn,Kn,Qn,Jn=Vn.prototype;function er(e,t,n){return Qn&&Qn.abort&&Qn.abort(),Qn=q(e,!0,n)}Jn._init=function(){var e=this;e.config=A(e),function(n){n._hooks={},n._lifecycle={},["init","mounted","beforeEach","afterEach","doneEach","ready"].forEach(function(e){var t=n._hooks[e]=[];n._lifecycle[e]=function(e){return t.push(e)}})}(e),function(t){[].concat(t.config.plugins).forEach(function(e){return r(e)&&e(t._lifecycle,t)})}(e),B(e,"init"),function(t){var e,n=t.config;e="history"===(n.routerMode||"hash")&&i?new Un(n):new qn(n),t.router=e,Zn(t),Bn=t.route,e.onchange(function(e){Zn(t),t._updateRender(),Bn.path!==t.route.path?(t.$fetch(p,t.$resetEvents.bind(t,e.source)),Bn=t.route):t.$resetEvents(e.source)})}(e),Dn(e),Wn(e),function(t){var e=t.config.loadSidebar;if(t.rendered){var n=Y(t.router,".sidebar-nav",!0,!0);e&&n&&(n.parentNode.innerHTML+=window.__SUB_SIDEBAR__),t._bindEventOnRendered(n),t.$resetEvents(),B(t,"doneEach"),B(t,"ready")}else t.$fetch(function(e){return B(t,"ready")})}(e),B(e,"mounted")},Jn.route={},(Xn=Jn)._renderTo=function(e,t,n){var r=m(e);r&&(r[n?"outerHTML":"innerHTML"]=t)},Xn._renderSidebar=function(e){var t=this.config,n=t.maxLevel,r=t.subMaxLevel,i=t.loadSidebar;if(t.hideSidebar)return document.querySelector("aside.sidebar").remove(),document.querySelector("button.sidebar-toggle").remove(),document.querySelector("section.content").style.right="unset",document.querySelector("section.content").style.left="unset",document.querySelector("section.content").style.position="relative",document.querySelector("section.content").style.width="100%",null;this._renderTo(".sidebar-nav",this.compiler.sidebar(e,n));var a=Y(this.router,".sidebar-nav",!0,!0);i&&a?a.parentNode.innerHTML+=this.compiler.subSidebar(r)||"":this.compiler.subSidebar(),this._bindEventOnRendered(a)},Xn._bindEventOnRendered=function(e){var t=this.config.autoHeader;if(function(e){var t=b(".cover.show");ge=t?t.offsetHeight:0;var n=m(".sidebar"),r=[];null!=n&&(r=y(n,"li"));for(var i=0,a=r.length;i<a;i+=1){var o=r[i],s=o.querySelector("a");if(s){var l=s.getAttribute("href");if("/"!==l){var c=e.parse(l),u=c.query.id,p=c.path;u&&(l=ve(p,u))}l&&(pe[decodeURIComponent(l)]=o)}}if(!h){var d=J(e.getCurrentPath());w("scroll",function(){return me(d)}),x("scroll",function(){return me(d)}),x(n,"mouseover",function(){de=!0}),x(n,"mouseleave",function(){de=!1})}}(this.router),t&&e){var n=m("#main"),r=n.children[0];r&&"H1"!==r.tagName&&k(n,g("div",this.compiler.header(e.innerText,1)).children[0])}},Xn._renderNav=function(e){e&&this._renderTo("nav",this.compiler.compile(e)),this.config.loadNavbar&&Y(this.router,"nav")},Xn._renderMain=function(r,i,a){var o=this;if(void 0===i&&(i={}),!r)return Pn.call(this,r);B(this,"beforeEach",r,function(e){function t(){i.updatedAt&&(n=Nn(n,i.updatedAt,o.config.formatUpdated)),B(o,"afterEach",n,function(e){return Pn.call(o,e)})}var n;o.isHTML?(n=o.result=r,t(),a()):On({compiler:o.compiler,raw:e},function(e){n=o.compiler.compile(e),n=o.isRemoteUrl?j.sanitize(n):n,t(),a()})})},Xn._renderCover=function(e,t){var n=m(".cover");if(_(m("main"),t?"add":"remove","hidden"),e){_(n,"add","show");var r=this.coverIsHTML?e:this.compiler.cover(e),i=r.trim().match('<p><img.*?data-origin="(.*?)"[^a]+alt="(.*?)">([^<]*?)</p>$');if(i){if("color"===i[2])n.style.background=i[1]+(i[3]||"");else{var a=i[1];_(n,"add","has-mask"),Q(i[1])||(a=re(this.router.getBasePath(),i[1])),n.style.backgroundImage="url("+a+")",n.style.backgroundSize="cover",n.style.backgroundPosition="center center"}r=r.replace(i[0],"")}this._renderTo(".cover-main",r),W()}else _(n,"remove","show")},Xn._updateRender=function(){!function(e){var t=m(".app-name-link"),n=e.config.nameLink,r=e.route.path;if(t)if(c(e.config.nameLink))t.setAttribute("href",n);else if("object"==typeof n){var i=Object.keys(n).filter(function(e){return-1<r.indexOf(e)})[0];t.setAttribute("href",n[i])}}(this)},(Kn=Jn)._loadSideAndNav=function(e,t,n,r){var i=this;return function(){if(!n)return r();Yn(e,t,n,function(e){i._renderSidebar(e),r()},i,!0)}},Kn._fetch=function(n){var r=this;void 0===n&&(n=p);var e=this.route,i=e.path,a=K(e.query,["id"]),t=this.config,o=t.loadNavbar,s=t.requestHeaders,l=t.loadSidebar,c=this.router.getFile(i),u=er(c+a,0,s);this.isRemoteUrl=function(e){var t=e.match(/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/);return"string"==typeof t[1]&&0<t[1].length&&t[1].toLowerCase()!==location.protocol||"string"==typeof t[2]&&0<t[2].length&&t[2].replace(new RegExp(":("+{"http:":80,"https:":443}[location.protocol]+")?$"),"")!==location.host}(c),this.isHTML=/\.html$/g.test(c),u.then(function(e,t){return r._renderMain(e,t,r._loadSideAndNav(i,a,l,n))},function(e){r._fetchFallbackPage(i,a,n)||r._fetch404(c,a,n)}),o&&Yn(i,a,o,function(e){return r._renderNav(e)},this,!0)},Kn._fetchCover=function(){var t=this,e=this.config,n=e.coverpage,r=e.requestHeaders,i=this.route.query,a=ee(this.route.path);if(n){var o=null,s=this.route.path;if("string"==typeof n)"/"===s&&(o=n);else if(Array.isArray(n))o=-1<n.indexOf(s)&&"_coverpage";else{var l=n[s];o=!0===l?"_coverpage":l}var c=Boolean(o)&&this.config.onlyCover;return o?(o=this.router.getFile(a+o),this.coverIsHTML=/\.html$/g.test(o),q(o+K(i,["id"]),!1,r).then(function(e){return t._renderCover(e,c)})):this._renderCover(null,c),c}},Kn.$fetch=function(e,t){var n=this;function r(){B(n,"doneEach"),e()}void 0===e&&(e=p),void 0===t&&(t=this.$resetEvents.bind(this)),this._fetchCover()?r():this._fetch(function(){t(),r()})},Kn._fetchFallbackPage=function(n,r,i){var a=this;void 0===i&&(i=p);var e=this.config,t=e.requestHeaders,o=e.fallbackLanguages,s=e.loadSidebar;if(!o)return!1;var l=n.split("/")[1];if(-1===o.indexOf(l))return!1;var c=this.router.getFile(n.replace(new RegExp("^/"+l),""));return er(c+r,0,t).then(function(e,t){return a._renderMain(e,t,a._loadSideAndNav(n,r,s,i))},function(){return a._fetch404(n,r,i)}),!0},Kn._fetch404=function(e,t,n){var r=this;void 0===n&&(n=p);var i=this.config,a=i.loadSidebar,o=i.requestHeaders,s=i.notFoundPage,l=this._loadSideAndNav(e,t,a,n);if(s){var c=function(t,e){var n,r,i=e.notFoundPage,a="_404"+(e.ext||".md");switch(typeof i){case"boolean":r=a;break;case"string":r=i;break;case"object":r=(n=Object.keys(i).sort(function(e,t){return t.length-e.length}).find(function(e){return t.match(new RegExp("^"+e))}))&&i[n]||a}return r}(e,this.config);return er(this.router.getFile(c),0,o).then(function(e,t){return r._renderMain(e,t,l)},function(){return r._renderMain(null,{},l)}),!0}return this._renderMain(null,{},l),!1},Jn.$resetEvents=function(e){var t=this,n=this.config.auto2top;"history"!==e&&(t.route.query.id&&be(t.route.path,t.route.query.id),"navigate"===e&&n&&function(e){void 0===e&&(e=0),ye.scrollTop=!0===e?0:Number(e)}(n)),this.config.loadNavbar&&Y(this.router,"nav")},window.Docsify={util:Gn,dom:t,get:q,slugify:_n,version:"4.11.6"},window.DocsifyCompiler=$n,window.marked=mn,window.Prism=En,e(function(e){return new Vn})}(); diff --git a/docs/_scripts/emoji.min.js b/docs/_scripts/emoji.min.js new file mode 100644 index 0000000..af23090 --- /dev/null +++ b/docs/_scripts/emoji.min.js @@ -0,0 +1 @@ +!function(){var o={100:"unicode/1f4af",1234:"unicode/1f522","+1":"unicode/1f44d","-1":"unicode/1f44e","1st_place_medal":"unicode/1f947","2nd_place_medal":"unicode/1f948","3rd_place_medal":"unicode/1f949","8ball":"unicode/1f3b1",a:"unicode/1f170",ab:"unicode/1f18e",abacus:"unicode/1f9ee",abc:"unicode/1f524",abcd:"unicode/1f521",accept:"unicode/1f251",adhesive_bandage:"unicode/1fa79",adult:"unicode/1f9d1",aerial_tramway:"unicode/1f6a1",afghanistan:"unicode/1f1e6-1f1eb",airplane:"unicode/2708",aland_islands:"unicode/1f1e6-1f1fd",alarm_clock:"unicode/23f0",albania:"unicode/1f1e6-1f1f1",alembic:"unicode/2697",algeria:"unicode/1f1e9-1f1ff",alien:"unicode/1f47d",ambulance:"unicode/1f691",american_samoa:"unicode/1f1e6-1f1f8",amphora:"unicode/1f3fa",anchor:"unicode/2693",andorra:"unicode/1f1e6-1f1e9",angel:"unicode/1f47c",anger:"unicode/1f4a2",angola:"unicode/1f1e6-1f1f4",angry:"unicode/1f620",anguilla:"unicode/1f1e6-1f1ee",anguished:"unicode/1f627",ant:"unicode/1f41c",antarctica:"unicode/1f1e6-1f1f6",antigua_barbuda:"unicode/1f1e6-1f1ec",apple:"unicode/1f34e",aquarius:"unicode/2652",argentina:"unicode/1f1e6-1f1f7",aries:"unicode/2648",armenia:"unicode/1f1e6-1f1f2",arrow_backward:"unicode/25c0",arrow_double_down:"unicode/23ec",arrow_double_up:"unicode/23eb",arrow_down:"unicode/2b07",arrow_down_small:"unicode/1f53d",arrow_forward:"unicode/25b6",arrow_heading_down:"unicode/2935",arrow_heading_up:"unicode/2934",arrow_left:"unicode/2b05",arrow_lower_left:"unicode/2199",arrow_lower_right:"unicode/2198",arrow_right:"unicode/27a1",arrow_right_hook:"unicode/21aa",arrow_up:"unicode/2b06",arrow_up_down:"unicode/2195",arrow_up_small:"unicode/1f53c",arrow_upper_left:"unicode/2196",arrow_upper_right:"unicode/2197",arrows_clockwise:"unicode/1f503",arrows_counterclockwise:"unicode/1f504",art:"unicode/1f3a8",articulated_lorry:"unicode/1f69b",artificial_satellite:"unicode/1f6f0",artist:"unicode/1f9d1-1f3a8",aruba:"unicode/1f1e6-1f1fc",ascension_island:"unicode/1f1e6-1f1e8",asterisk:"unicode/002a-20e3",astonished:"unicode/1f632",astronaut:"unicode/1f9d1-1f680",athletic_shoe:"unicode/1f45f",atm:"unicode/1f3e7",atom:"atom",atom_symbol:"unicode/269b",australia:"unicode/1f1e6-1f1fa",austria:"unicode/1f1e6-1f1f9",auto_rickshaw:"unicode/1f6fa",avocado:"unicode/1f951",axe:"unicode/1fa93",azerbaijan:"unicode/1f1e6-1f1ff",b:"unicode/1f171",baby:"unicode/1f476",baby_bottle:"unicode/1f37c",baby_chick:"unicode/1f424",baby_symbol:"unicode/1f6bc",back:"unicode/1f519",bacon:"unicode/1f953",badger:"unicode/1f9a1",badminton:"unicode/1f3f8",bagel:"unicode/1f96f",baggage_claim:"unicode/1f6c4",baguette_bread:"unicode/1f956",bahamas:"unicode/1f1e7-1f1f8",bahrain:"unicode/1f1e7-1f1ed",balance_scale:"unicode/2696",bald_man:"unicode/1f468-1f9b2",bald_woman:"unicode/1f469-1f9b2",ballet_shoes:"unicode/1fa70",balloon:"unicode/1f388",ballot_box:"unicode/1f5f3",ballot_box_with_check:"unicode/2611",bamboo:"unicode/1f38d",banana:"unicode/1f34c",bangbang:"unicode/203c",bangladesh:"unicode/1f1e7-1f1e9",banjo:"unicode/1fa95",bank:"unicode/1f3e6",bar_chart:"unicode/1f4ca",barbados:"unicode/1f1e7-1f1e7",barber:"unicode/1f488",baseball:"unicode/26be",basecamp:"basecamp",basecampy:"basecampy",basket:"unicode/1f9fa",basketball:"unicode/1f3c0",basketball_man:"unicode/26f9-2642",basketball_woman:"unicode/26f9-2640",bat:"unicode/1f987",bath:"unicode/1f6c0",bathtub:"unicode/1f6c1",battery:"unicode/1f50b",beach_umbrella:"unicode/1f3d6",bear:"unicode/1f43b",bearded_person:"unicode/1f9d4",bed:"unicode/1f6cf",bee:"unicode/1f41d",beer:"unicode/1f37a",beers:"unicode/1f37b",beetle:"unicode/1f41e",beginner:"unicode/1f530",belarus:"unicode/1f1e7-1f1fe",belgium:"unicode/1f1e7-1f1ea",belize:"unicode/1f1e7-1f1ff",bell:"unicode/1f514",bellhop_bell:"unicode/1f6ce",benin:"unicode/1f1e7-1f1ef",bento:"unicode/1f371",bermuda:"unicode/1f1e7-1f1f2",beverage_box:"unicode/1f9c3",bhutan:"unicode/1f1e7-1f1f9",bicyclist:"unicode/1f6b4",bike:"unicode/1f6b2",biking_man:"unicode/1f6b4-2642",biking_woman:"unicode/1f6b4-2640",bikini:"unicode/1f459",billed_cap:"unicode/1f9e2",biohazard:"unicode/2623",bird:"unicode/1f426",birthday:"unicode/1f382",black_circle:"unicode/26ab",black_flag:"unicode/1f3f4",black_heart:"unicode/1f5a4",black_joker:"unicode/1f0cf",black_large_square:"unicode/2b1b",black_medium_small_square:"unicode/25fe",black_medium_square:"unicode/25fc",black_nib:"unicode/2712",black_small_square:"unicode/25aa",black_square_button:"unicode/1f532",blond_haired_man:"unicode/1f471-2642",blond_haired_person:"unicode/1f471",blond_haired_woman:"unicode/1f471-2640",blonde_woman:"unicode/1f471-2640",blossom:"unicode/1f33c",blowfish:"unicode/1f421",blue_book:"unicode/1f4d8",blue_car:"unicode/1f699",blue_heart:"unicode/1f499",blue_square:"unicode/1f7e6",blush:"unicode/1f60a",boar:"unicode/1f417",boat:"unicode/26f5",bolivia:"unicode/1f1e7-1f1f4",bomb:"unicode/1f4a3",bone:"unicode/1f9b4",book:"unicode/1f4d6",bookmark:"unicode/1f516",bookmark_tabs:"unicode/1f4d1",books:"unicode/1f4da",boom:"unicode/1f4a5",boot:"unicode/1f462",bosnia_herzegovina:"unicode/1f1e7-1f1e6",botswana:"unicode/1f1e7-1f1fc",bouncing_ball_man:"unicode/26f9-2642",bouncing_ball_person:"unicode/26f9",bouncing_ball_woman:"unicode/26f9-2640",bouquet:"unicode/1f490",bouvet_island:"unicode/1f1e7-1f1fb",bow:"unicode/1f647",bow_and_arrow:"unicode/1f3f9",bowing_man:"unicode/1f647-2642",bowing_woman:"unicode/1f647-2640",bowl_with_spoon:"unicode/1f963",bowling:"unicode/1f3b3",bowtie:"bowtie",boxing_glove:"unicode/1f94a",boy:"unicode/1f466",brain:"unicode/1f9e0",brazil:"unicode/1f1e7-1f1f7",bread:"unicode/1f35e",breast_feeding:"unicode/1f931",bricks:"unicode/1f9f1",bride_with_veil:"unicode/1f470",bridge_at_night:"unicode/1f309",briefcase:"unicode/1f4bc",british_indian_ocean_territory:"unicode/1f1ee-1f1f4",british_virgin_islands:"unicode/1f1fb-1f1ec",broccoli:"unicode/1f966",broken_heart:"unicode/1f494",broom:"unicode/1f9f9",brown_circle:"unicode/1f7e4",brown_heart:"unicode/1f90e",brown_square:"unicode/1f7eb",brunei:"unicode/1f1e7-1f1f3",bug:"unicode/1f41b",building_construction:"unicode/1f3d7",bulb:"unicode/1f4a1",bulgaria:"unicode/1f1e7-1f1ec",bullettrain_front:"unicode/1f685",bullettrain_side:"unicode/1f684",burkina_faso:"unicode/1f1e7-1f1eb",burrito:"unicode/1f32f",burundi:"unicode/1f1e7-1f1ee",bus:"unicode/1f68c",business_suit_levitating:"unicode/1f574",busstop:"unicode/1f68f",bust_in_silhouette:"unicode/1f464",busts_in_silhouette:"unicode/1f465",butter:"unicode/1f9c8",butterfly:"unicode/1f98b",cactus:"unicode/1f335",cake:"unicode/1f370",calendar:"unicode/1f4c6",call_me_hand:"unicode/1f919",calling:"unicode/1f4f2",cambodia:"unicode/1f1f0-1f1ed",camel:"unicode/1f42b",camera:"unicode/1f4f7",camera_flash:"unicode/1f4f8",cameroon:"unicode/1f1e8-1f1f2",camping:"unicode/1f3d5",canada:"unicode/1f1e8-1f1e6",canary_islands:"unicode/1f1ee-1f1e8",cancer:"unicode/264b",candle:"unicode/1f56f",candy:"unicode/1f36c",canned_food:"unicode/1f96b",canoe:"unicode/1f6f6",cape_verde:"unicode/1f1e8-1f1fb",capital_abcd:"unicode/1f520",capricorn:"unicode/2651",car:"unicode/1f697",card_file_box:"unicode/1f5c3",card_index:"unicode/1f4c7",card_index_dividers:"unicode/1f5c2",caribbean_netherlands:"unicode/1f1e7-1f1f6",carousel_horse:"unicode/1f3a0",carrot:"unicode/1f955",cartwheeling:"unicode/1f938",cat:"unicode/1f431",cat2:"unicode/1f408",cayman_islands:"unicode/1f1f0-1f1fe",cd:"unicode/1f4bf",central_african_republic:"unicode/1f1e8-1f1eb",ceuta_melilla:"unicode/1f1ea-1f1e6",chad:"unicode/1f1f9-1f1e9",chains:"unicode/26d3",chair:"unicode/1fa91",champagne:"unicode/1f37e",chart:"unicode/1f4b9",chart_with_downwards_trend:"unicode/1f4c9",chart_with_upwards_trend:"unicode/1f4c8",checkered_flag:"unicode/1f3c1",cheese:"unicode/1f9c0",cherries:"unicode/1f352",cherry_blossom:"unicode/1f338",chess_pawn:"unicode/265f",chestnut:"unicode/1f330",chicken:"unicode/1f414",child:"unicode/1f9d2",children_crossing:"unicode/1f6b8",chile:"unicode/1f1e8-1f1f1",chipmunk:"unicode/1f43f",chocolate_bar:"unicode/1f36b",chopsticks:"unicode/1f962",christmas_island:"unicode/1f1e8-1f1fd",christmas_tree:"unicode/1f384",church:"unicode/26ea",cinema:"unicode/1f3a6",circus_tent:"unicode/1f3aa",city_sunrise:"unicode/1f307",city_sunset:"unicode/1f306",cityscape:"unicode/1f3d9",cl:"unicode/1f191",clamp:"unicode/1f5dc",clap:"unicode/1f44f",clapper:"unicode/1f3ac",classical_building:"unicode/1f3db",climbing:"unicode/1f9d7",climbing_man:"unicode/1f9d7-2642",climbing_woman:"unicode/1f9d7-2640",clinking_glasses:"unicode/1f942",clipboard:"unicode/1f4cb",clipperton_island:"unicode/1f1e8-1f1f5",clock1:"unicode/1f550",clock10:"unicode/1f559",clock1030:"unicode/1f565",clock11:"unicode/1f55a",clock1130:"unicode/1f566",clock12:"unicode/1f55b",clock1230:"unicode/1f567",clock130:"unicode/1f55c",clock2:"unicode/1f551",clock230:"unicode/1f55d",clock3:"unicode/1f552",clock330:"unicode/1f55e",clock4:"unicode/1f553",clock430:"unicode/1f55f",clock5:"unicode/1f554",clock530:"unicode/1f560",clock6:"unicode/1f555",clock630:"unicode/1f561",clock7:"unicode/1f556",clock730:"unicode/1f562",clock8:"unicode/1f557",clock830:"unicode/1f563",clock9:"unicode/1f558",clock930:"unicode/1f564",closed_book:"unicode/1f4d5",closed_lock_with_key:"unicode/1f510",closed_umbrella:"unicode/1f302",cloud:"unicode/2601",cloud_with_lightning:"unicode/1f329",cloud_with_lightning_and_rain:"unicode/26c8",cloud_with_rain:"unicode/1f327",cloud_with_snow:"unicode/1f328",clown_face:"unicode/1f921",clubs:"unicode/2663",cn:"unicode/1f1e8-1f1f3",coat:"unicode/1f9e5",cocktail:"unicode/1f378",coconut:"unicode/1f965",cocos_islands:"unicode/1f1e8-1f1e8",coffee:"unicode/2615",coffin:"unicode/26b0",cold_face:"unicode/1f976",cold_sweat:"unicode/1f630",collision:"unicode/1f4a5",colombia:"unicode/1f1e8-1f1f4",comet:"unicode/2604",comoros:"unicode/1f1f0-1f1f2",compass:"unicode/1f9ed",computer:"unicode/1f4bb",computer_mouse:"unicode/1f5b1",confetti_ball:"unicode/1f38a",confounded:"unicode/1f616",confused:"unicode/1f615",congo_brazzaville:"unicode/1f1e8-1f1ec",congo_kinshasa:"unicode/1f1e8-1f1e9",congratulations:"unicode/3297",construction:"unicode/1f6a7",construction_worker:"unicode/1f477",construction_worker_man:"unicode/1f477-2642",construction_worker_woman:"unicode/1f477-2640",control_knobs:"unicode/1f39b",convenience_store:"unicode/1f3ea",cook:"unicode/1f9d1-1f373",cook_islands:"unicode/1f1e8-1f1f0",cookie:"unicode/1f36a",cool:"unicode/1f192",cop:"unicode/1f46e",copyright:"unicode/00a9",corn:"unicode/1f33d",costa_rica:"unicode/1f1e8-1f1f7",cote_divoire:"unicode/1f1e8-1f1ee",couch_and_lamp:"unicode/1f6cb",couple:"unicode/1f46b",couple_with_heart:"unicode/1f491",couple_with_heart_man_man:"unicode/1f468-2764-1f468",couple_with_heart_woman_man:"unicode/1f469-2764-1f468",couple_with_heart_woman_woman:"unicode/1f469-2764-1f469",couplekiss:"unicode/1f48f",couplekiss_man_man:"unicode/1f468-2764-1f48b-1f468",couplekiss_man_woman:"unicode/1f469-2764-1f48b-1f468",couplekiss_woman_woman:"unicode/1f469-2764-1f48b-1f469",cow:"unicode/1f42e",cow2:"unicode/1f404",cowboy_hat_face:"unicode/1f920",crab:"unicode/1f980",crayon:"unicode/1f58d",credit_card:"unicode/1f4b3",crescent_moon:"unicode/1f319",cricket:"unicode/1f997",cricket_game:"unicode/1f3cf",croatia:"unicode/1f1ed-1f1f7",crocodile:"unicode/1f40a",croissant:"unicode/1f950",crossed_fingers:"unicode/1f91e",crossed_flags:"unicode/1f38c",crossed_swords:"unicode/2694",crown:"unicode/1f451",cry:"unicode/1f622",crying_cat_face:"unicode/1f63f",crystal_ball:"unicode/1f52e",cuba:"unicode/1f1e8-1f1fa",cucumber:"unicode/1f952",cup_with_straw:"unicode/1f964",cupcake:"unicode/1f9c1",cupid:"unicode/1f498",curacao:"unicode/1f1e8-1f1fc",curling_stone:"unicode/1f94c",curly_haired_man:"unicode/1f468-1f9b1",curly_haired_woman:"unicode/1f469-1f9b1",curly_loop:"unicode/27b0",currency_exchange:"unicode/1f4b1",curry:"unicode/1f35b",cursing_face:"unicode/1f92c",custard:"unicode/1f36e",customs:"unicode/1f6c3",cut_of_meat:"unicode/1f969",cyclone:"unicode/1f300",cyprus:"unicode/1f1e8-1f1fe",czech_republic:"unicode/1f1e8-1f1ff",dagger:"unicode/1f5e1",dancer:"unicode/1f483",dancers:"unicode/1f46f",dancing_men:"unicode/1f46f-2642",dancing_women:"unicode/1f46f-2640",dango:"unicode/1f361",dark_sunglasses:"unicode/1f576",dart:"unicode/1f3af",dash:"unicode/1f4a8",date:"unicode/1f4c5",de:"unicode/1f1e9-1f1ea",deaf_man:"unicode/1f9cf-2642",deaf_person:"unicode/1f9cf",deaf_woman:"unicode/1f9cf-2640",deciduous_tree:"unicode/1f333",deer:"unicode/1f98c",denmark:"unicode/1f1e9-1f1f0",department_store:"unicode/1f3ec",derelict_house:"unicode/1f3da",desert:"unicode/1f3dc",desert_island:"unicode/1f3dd",desktop_computer:"unicode/1f5a5",detective:"unicode/1f575",diamond_shape_with_a_dot_inside:"unicode/1f4a0",diamonds:"unicode/2666",diego_garcia:"unicode/1f1e9-1f1ec",disappointed:"unicode/1f61e",disappointed_relieved:"unicode/1f625",diving_mask:"unicode/1f93f",diya_lamp:"unicode/1fa94",dizzy:"unicode/1f4ab",dizzy_face:"unicode/1f635",djibouti:"unicode/1f1e9-1f1ef",dna:"unicode/1f9ec",do_not_litter:"unicode/1f6af",dog:"unicode/1f436",dog2:"unicode/1f415",dollar:"unicode/1f4b5",dolls:"unicode/1f38e",dolphin:"unicode/1f42c",dominica:"unicode/1f1e9-1f1f2",dominican_republic:"unicode/1f1e9-1f1f4",door:"unicode/1f6aa",doughnut:"unicode/1f369",dove:"unicode/1f54a",dragon:"unicode/1f409",dragon_face:"unicode/1f432",dress:"unicode/1f457",dromedary_camel:"unicode/1f42a",drooling_face:"unicode/1f924",drop_of_blood:"unicode/1fa78",droplet:"unicode/1f4a7",drum:"unicode/1f941",duck:"unicode/1f986",dumpling:"unicode/1f95f",dvd:"unicode/1f4c0","e-mail":"unicode/1f4e7",eagle:"unicode/1f985",ear:"unicode/1f442",ear_of_rice:"unicode/1f33e",ear_with_hearing_aid:"unicode/1f9bb",earth_africa:"unicode/1f30d",earth_americas:"unicode/1f30e",earth_asia:"unicode/1f30f",ecuador:"unicode/1f1ea-1f1e8",egg:"unicode/1f95a",eggplant:"unicode/1f346",egypt:"unicode/1f1ea-1f1ec",eight:"unicode/0038-20e3",eight_pointed_black_star:"unicode/2734",eight_spoked_asterisk:"unicode/2733",eject_button:"unicode/23cf",el_salvador:"unicode/1f1f8-1f1fb",electric_plug:"unicode/1f50c",electron:"electron",elephant:"unicode/1f418",elf:"unicode/1f9dd",elf_man:"unicode/1f9dd-2642",elf_woman:"unicode/1f9dd-2640",email:"unicode/2709",end:"unicode/1f51a",england:"unicode/1f3f4-e0067-e0062-e0065-e006e-e0067-e007f",envelope:"unicode/2709",envelope_with_arrow:"unicode/1f4e9",equatorial_guinea:"unicode/1f1ec-1f1f6",eritrea:"unicode/1f1ea-1f1f7",es:"unicode/1f1ea-1f1f8",estonia:"unicode/1f1ea-1f1ea",ethiopia:"unicode/1f1ea-1f1f9",eu:"unicode/1f1ea-1f1fa",euro:"unicode/1f4b6",european_castle:"unicode/1f3f0",european_post_office:"unicode/1f3e4",european_union:"unicode/1f1ea-1f1fa",evergreen_tree:"unicode/1f332",exclamation:"unicode/2757",exploding_head:"unicode/1f92f",expressionless:"unicode/1f611",eye:"unicode/1f441",eye_speech_bubble:"unicode/1f441-1f5e8",eyeglasses:"unicode/1f453",eyes:"unicode/1f440",face_with_head_bandage:"unicode/1f915",face_with_thermometer:"unicode/1f912",facepalm:"unicode/1f926",facepunch:"unicode/1f44a",factory:"unicode/1f3ed",factory_worker:"unicode/1f9d1-1f3ed",fairy:"unicode/1f9da",fairy_man:"unicode/1f9da-2642",fairy_woman:"unicode/1f9da-2640",falafel:"unicode/1f9c6",falkland_islands:"unicode/1f1eb-1f1f0",fallen_leaf:"unicode/1f342",family:"unicode/1f46a",family_man_boy:"unicode/1f468-1f466",family_man_boy_boy:"unicode/1f468-1f466-1f466",family_man_girl:"unicode/1f468-1f467",family_man_girl_boy:"unicode/1f468-1f467-1f466",family_man_girl_girl:"unicode/1f468-1f467-1f467",family_man_man_boy:"unicode/1f468-1f468-1f466",family_man_man_boy_boy:"unicode/1f468-1f468-1f466-1f466",family_man_man_girl:"unicode/1f468-1f468-1f467",family_man_man_girl_boy:"unicode/1f468-1f468-1f467-1f466",family_man_man_girl_girl:"unicode/1f468-1f468-1f467-1f467",family_man_woman_boy:"unicode/1f468-1f469-1f466",family_man_woman_boy_boy:"unicode/1f468-1f469-1f466-1f466",family_man_woman_girl:"unicode/1f468-1f469-1f467",family_man_woman_girl_boy:"unicode/1f468-1f469-1f467-1f466",family_man_woman_girl_girl:"unicode/1f468-1f469-1f467-1f467",family_woman_boy:"unicode/1f469-1f466",family_woman_boy_boy:"unicode/1f469-1f466-1f466",family_woman_girl:"unicode/1f469-1f467",family_woman_girl_boy:"unicode/1f469-1f467-1f466",family_woman_girl_girl:"unicode/1f469-1f467-1f467",family_woman_woman_boy:"unicode/1f469-1f469-1f466",family_woman_woman_boy_boy:"unicode/1f469-1f469-1f466-1f466",family_woman_woman_girl:"unicode/1f469-1f469-1f467",family_woman_woman_girl_boy:"unicode/1f469-1f469-1f467-1f466",family_woman_woman_girl_girl:"unicode/1f469-1f469-1f467-1f467",farmer:"unicode/1f9d1-1f33e",faroe_islands:"unicode/1f1eb-1f1f4",fast_forward:"unicode/23e9",fax:"unicode/1f4e0",fearful:"unicode/1f628",feelsgood:"feelsgood",feet:"unicode/1f43e",female_detective:"unicode/1f575-2640",female_sign:"unicode/2640",ferris_wheel:"unicode/1f3a1",ferry:"unicode/26f4",field_hockey:"unicode/1f3d1",fiji:"unicode/1f1eb-1f1ef",file_cabinet:"unicode/1f5c4",file_folder:"unicode/1f4c1",film_projector:"unicode/1f4fd",film_strip:"unicode/1f39e",finland:"unicode/1f1eb-1f1ee",finnadie:"finnadie",fire:"unicode/1f525",fire_engine:"unicode/1f692",fire_extinguisher:"unicode/1f9ef",firecracker:"unicode/1f9e8",firefighter:"unicode/1f9d1-1f692",fireworks:"unicode/1f386",first_quarter_moon:"unicode/1f313",first_quarter_moon_with_face:"unicode/1f31b",fish:"unicode/1f41f",fish_cake:"unicode/1f365",fishing_pole_and_fish:"unicode/1f3a3",fist:"unicode/270a",fist_left:"unicode/1f91b",fist_oncoming:"unicode/1f44a",fist_raised:"unicode/270a",fist_right:"unicode/1f91c",five:"unicode/0035-20e3",flags:"unicode/1f38f",flamingo:"unicode/1f9a9",flashlight:"unicode/1f526",flat_shoe:"unicode/1f97f",fleur_de_lis:"unicode/269c",flight_arrival:"unicode/1f6ec",flight_departure:"unicode/1f6eb",flipper:"unicode/1f42c",floppy_disk:"unicode/1f4be",flower_playing_cards:"unicode/1f3b4",flushed:"unicode/1f633",flying_disc:"unicode/1f94f",flying_saucer:"unicode/1f6f8",fog:"unicode/1f32b",foggy:"unicode/1f301",foot:"unicode/1f9b6",football:"unicode/1f3c8",footprints:"unicode/1f463",fork_and_knife:"unicode/1f374",fortune_cookie:"unicode/1f960",fountain:"unicode/26f2",fountain_pen:"unicode/1f58b",four:"unicode/0034-20e3",four_leaf_clover:"unicode/1f340",fox_face:"unicode/1f98a",fr:"unicode/1f1eb-1f1f7",framed_picture:"unicode/1f5bc",free:"unicode/1f193",french_guiana:"unicode/1f1ec-1f1eb",french_polynesia:"unicode/1f1f5-1f1eb",french_southern_territories:"unicode/1f1f9-1f1eb",fried_egg:"unicode/1f373",fried_shrimp:"unicode/1f364",fries:"unicode/1f35f",frog:"unicode/1f438",frowning:"unicode/1f626",frowning_face:"unicode/2639",frowning_man:"unicode/1f64d-2642",frowning_person:"unicode/1f64d",frowning_woman:"unicode/1f64d-2640",fu:"unicode/1f595",fuelpump:"unicode/26fd",full_moon:"unicode/1f315",full_moon_with_face:"unicode/1f31d",funeral_urn:"unicode/26b1",gabon:"unicode/1f1ec-1f1e6",gambia:"unicode/1f1ec-1f1f2",game_die:"unicode/1f3b2",garlic:"unicode/1f9c4",gb:"unicode/1f1ec-1f1e7",gear:"unicode/2699",gem:"unicode/1f48e",gemini:"unicode/264a",genie:"unicode/1f9de",genie_man:"unicode/1f9de-2642",genie_woman:"unicode/1f9de-2640",georgia:"unicode/1f1ec-1f1ea",ghana:"unicode/1f1ec-1f1ed",ghost:"unicode/1f47b",gibraltar:"unicode/1f1ec-1f1ee",gift:"unicode/1f381",gift_heart:"unicode/1f49d",giraffe:"unicode/1f992",girl:"unicode/1f467",globe_with_meridians:"unicode/1f310",gloves:"unicode/1f9e4",goal_net:"unicode/1f945",goat:"unicode/1f410",goberserk:"goberserk",godmode:"godmode",goggles:"unicode/1f97d",golf:"unicode/26f3",golfing:"unicode/1f3cc",golfing_man:"unicode/1f3cc-2642",golfing_woman:"unicode/1f3cc-2640",gorilla:"unicode/1f98d",grapes:"unicode/1f347",greece:"unicode/1f1ec-1f1f7",green_apple:"unicode/1f34f",green_book:"unicode/1f4d7",green_circle:"unicode/1f7e2",green_heart:"unicode/1f49a",green_salad:"unicode/1f957",green_square:"unicode/1f7e9",greenland:"unicode/1f1ec-1f1f1",grenada:"unicode/1f1ec-1f1e9",grey_exclamation:"unicode/2755",grey_question:"unicode/2754",grimacing:"unicode/1f62c",grin:"unicode/1f601",grinning:"unicode/1f600",guadeloupe:"unicode/1f1ec-1f1f5",guam:"unicode/1f1ec-1f1fa",guard:"unicode/1f482",guardsman:"unicode/1f482-2642",guardswoman:"unicode/1f482-2640",guatemala:"unicode/1f1ec-1f1f9",guernsey:"unicode/1f1ec-1f1ec",guide_dog:"unicode/1f9ae",guinea:"unicode/1f1ec-1f1f3",guinea_bissau:"unicode/1f1ec-1f1fc",guitar:"unicode/1f3b8",gun:"unicode/1f52b",guyana:"unicode/1f1ec-1f1fe",haircut:"unicode/1f487",haircut_man:"unicode/1f487-2642",haircut_woman:"unicode/1f487-2640",haiti:"unicode/1f1ed-1f1f9",hamburger:"unicode/1f354",hammer:"unicode/1f528",hammer_and_pick:"unicode/2692",hammer_and_wrench:"unicode/1f6e0",hamster:"unicode/1f439",hand:"unicode/270b",hand_over_mouth:"unicode/1f92d",handbag:"unicode/1f45c",handball_person:"unicode/1f93e",handshake:"unicode/1f91d",hankey:"unicode/1f4a9",hash:"unicode/0023-20e3",hatched_chick:"unicode/1f425",hatching_chick:"unicode/1f423",headphones:"unicode/1f3a7",health_worker:"unicode/1f9d1-2695",hear_no_evil:"unicode/1f649",heard_mcdonald_islands:"unicode/1f1ed-1f1f2",heart:"unicode/2764",heart_decoration:"unicode/1f49f",heart_eyes:"unicode/1f60d",heart_eyes_cat:"unicode/1f63b",heartbeat:"unicode/1f493",heartpulse:"unicode/1f497",hearts:"unicode/2665",heavy_check_mark:"unicode/2714",heavy_division_sign:"unicode/2797",heavy_dollar_sign:"unicode/1f4b2",heavy_exclamation_mark:"unicode/2757",heavy_heart_exclamation:"unicode/2763",heavy_minus_sign:"unicode/2796",heavy_multiplication_x:"unicode/2716",heavy_plus_sign:"unicode/2795",hedgehog:"unicode/1f994",helicopter:"unicode/1f681",herb:"unicode/1f33f",hibiscus:"unicode/1f33a",high_brightness:"unicode/1f506",high_heel:"unicode/1f460",hiking_boot:"unicode/1f97e",hindu_temple:"unicode/1f6d5",hippopotamus:"unicode/1f99b",hocho:"unicode/1f52a",hole:"unicode/1f573",honduras:"unicode/1f1ed-1f1f3",honey_pot:"unicode/1f36f",honeybee:"unicode/1f41d",hong_kong:"unicode/1f1ed-1f1f0",horse:"unicode/1f434",horse_racing:"unicode/1f3c7",hospital:"unicode/1f3e5",hot_face:"unicode/1f975",hot_pepper:"unicode/1f336",hotdog:"unicode/1f32d",hotel:"unicode/1f3e8",hotsprings:"unicode/2668",hourglass:"unicode/231b",hourglass_flowing_sand:"unicode/23f3",house:"unicode/1f3e0",house_with_garden:"unicode/1f3e1",houses:"unicode/1f3d8",hugs:"unicode/1f917",hungary:"unicode/1f1ed-1f1fa",hurtrealbad:"hurtrealbad",hushed:"unicode/1f62f",ice_cream:"unicode/1f368",ice_cube:"unicode/1f9ca",ice_hockey:"unicode/1f3d2",ice_skate:"unicode/26f8",icecream:"unicode/1f366",iceland:"unicode/1f1ee-1f1f8",id:"unicode/1f194",ideograph_advantage:"unicode/1f250",imp:"unicode/1f47f",inbox_tray:"unicode/1f4e5",incoming_envelope:"unicode/1f4e8",india:"unicode/1f1ee-1f1f3",indonesia:"unicode/1f1ee-1f1e9",infinity:"unicode/267e",information_desk_person:"unicode/1f481",information_source:"unicode/2139",innocent:"unicode/1f607",interrobang:"unicode/2049",iphone:"unicode/1f4f1",iran:"unicode/1f1ee-1f1f7",iraq:"unicode/1f1ee-1f1f6",ireland:"unicode/1f1ee-1f1ea",isle_of_man:"unicode/1f1ee-1f1f2",israel:"unicode/1f1ee-1f1f1",it:"unicode/1f1ee-1f1f9",izakaya_lantern:"unicode/1f3ee",jack_o_lantern:"unicode/1f383",jamaica:"unicode/1f1ef-1f1f2",japan:"unicode/1f5fe",japanese_castle:"unicode/1f3ef",japanese_goblin:"unicode/1f47a",japanese_ogre:"unicode/1f479",jeans:"unicode/1f456",jersey:"unicode/1f1ef-1f1ea",jigsaw:"unicode/1f9e9",jordan:"unicode/1f1ef-1f1f4",joy:"unicode/1f602",joy_cat:"unicode/1f639",joystick:"unicode/1f579",jp:"unicode/1f1ef-1f1f5",judge:"unicode/1f9d1-2696",juggling_person:"unicode/1f939",kaaba:"unicode/1f54b",kangaroo:"unicode/1f998",kazakhstan:"unicode/1f1f0-1f1ff",kenya:"unicode/1f1f0-1f1ea",key:"unicode/1f511",keyboard:"unicode/2328",keycap_ten:"unicode/1f51f",kick_scooter:"unicode/1f6f4",kimono:"unicode/1f458",kiribati:"unicode/1f1f0-1f1ee",kiss:"unicode/1f48b",kissing:"unicode/1f617",kissing_cat:"unicode/1f63d",kissing_closed_eyes:"unicode/1f61a",kissing_heart:"unicode/1f618",kissing_smiling_eyes:"unicode/1f619",kite:"unicode/1fa81",kiwi_fruit:"unicode/1f95d",kneeling_man:"unicode/1f9ce-2642",kneeling_person:"unicode/1f9ce",kneeling_woman:"unicode/1f9ce-2640",knife:"unicode/1f52a",koala:"unicode/1f428",koko:"unicode/1f201",kosovo:"unicode/1f1fd-1f1f0",kr:"unicode/1f1f0-1f1f7",kuwait:"unicode/1f1f0-1f1fc",kyrgyzstan:"unicode/1f1f0-1f1ec",lab_coat:"unicode/1f97c",label:"unicode/1f3f7",lacrosse:"unicode/1f94d",lantern:"unicode/1f3ee",laos:"unicode/1f1f1-1f1e6",large_blue_circle:"unicode/1f535",large_blue_diamond:"unicode/1f537",large_orange_diamond:"unicode/1f536",last_quarter_moon:"unicode/1f317",last_quarter_moon_with_face:"unicode/1f31c",latin_cross:"unicode/271d",latvia:"unicode/1f1f1-1f1fb",laughing:"unicode/1f606",leafy_green:"unicode/1f96c",leaves:"unicode/1f343",lebanon:"unicode/1f1f1-1f1e7",ledger:"unicode/1f4d2",left_luggage:"unicode/1f6c5",left_right_arrow:"unicode/2194",left_speech_bubble:"unicode/1f5e8",leftwards_arrow_with_hook:"unicode/21a9",leg:"unicode/1f9b5",lemon:"unicode/1f34b",leo:"unicode/264c",leopard:"unicode/1f406",lesotho:"unicode/1f1f1-1f1f8",level_slider:"unicode/1f39a",liberia:"unicode/1f1f1-1f1f7",libra:"unicode/264e",libya:"unicode/1f1f1-1f1fe",liechtenstein:"unicode/1f1f1-1f1ee",light_rail:"unicode/1f688",link:"unicode/1f517",lion:"unicode/1f981",lips:"unicode/1f444",lipstick:"unicode/1f484",lithuania:"unicode/1f1f1-1f1f9",lizard:"unicode/1f98e",llama:"unicode/1f999",lobster:"unicode/1f99e",lock:"unicode/1f512",lock_with_ink_pen:"unicode/1f50f",lollipop:"unicode/1f36d",loop:"unicode/27bf",lotion_bottle:"unicode/1f9f4",lotus_position:"unicode/1f9d8",lotus_position_man:"unicode/1f9d8-2642",lotus_position_woman:"unicode/1f9d8-2640",loud_sound:"unicode/1f50a",loudspeaker:"unicode/1f4e2",love_hotel:"unicode/1f3e9",love_letter:"unicode/1f48c",love_you_gesture:"unicode/1f91f",low_brightness:"unicode/1f505",luggage:"unicode/1f9f3",luxembourg:"unicode/1f1f1-1f1fa",lying_face:"unicode/1f925",m:"unicode/24c2",macau:"unicode/1f1f2-1f1f4",macedonia:"unicode/1f1f2-1f1f0",madagascar:"unicode/1f1f2-1f1ec",mag:"unicode/1f50d",mag_right:"unicode/1f50e",mage:"unicode/1f9d9",mage_man:"unicode/1f9d9-2642",mage_woman:"unicode/1f9d9-2640",magnet:"unicode/1f9f2",mahjong:"unicode/1f004",mailbox:"unicode/1f4eb",mailbox_closed:"unicode/1f4ea",mailbox_with_mail:"unicode/1f4ec",mailbox_with_no_mail:"unicode/1f4ed",malawi:"unicode/1f1f2-1f1fc",malaysia:"unicode/1f1f2-1f1fe",maldives:"unicode/1f1f2-1f1fb",male_detective:"unicode/1f575-2642",male_sign:"unicode/2642",mali:"unicode/1f1f2-1f1f1",malta:"unicode/1f1f2-1f1f9",man:"unicode/1f468",man_artist:"unicode/1f468-1f3a8",man_astronaut:"unicode/1f468-1f680",man_cartwheeling:"unicode/1f938-2642",man_cook:"unicode/1f468-1f373",man_dancing:"unicode/1f57a",man_facepalming:"unicode/1f926-2642",man_factory_worker:"unicode/1f468-1f3ed",man_farmer:"unicode/1f468-1f33e",man_firefighter:"unicode/1f468-1f692",man_health_worker:"unicode/1f468-2695",man_in_manual_wheelchair:"unicode/1f468-1f9bd",man_in_motorized_wheelchair:"unicode/1f468-1f9bc",man_in_tuxedo:"unicode/1f935",man_judge:"unicode/1f468-2696",man_juggling:"unicode/1f939-2642",man_mechanic:"unicode/1f468-1f527",man_office_worker:"unicode/1f468-1f4bc",man_pilot:"unicode/1f468-2708",man_playing_handball:"unicode/1f93e-2642",man_playing_water_polo:"unicode/1f93d-2642",man_scientist:"unicode/1f468-1f52c",man_shrugging:"unicode/1f937-2642",man_singer:"unicode/1f468-1f3a4",man_student:"unicode/1f468-1f393",man_teacher:"unicode/1f468-1f3eb",man_technologist:"unicode/1f468-1f4bb",man_with_gua_pi_mao:"unicode/1f472",man_with_probing_cane:"unicode/1f468-1f9af",man_with_turban:"unicode/1f473-2642",mandarin:"unicode/1f34a",mango:"unicode/1f96d",mans_shoe:"unicode/1f45e",mantelpiece_clock:"unicode/1f570",manual_wheelchair:"unicode/1f9bd",maple_leaf:"unicode/1f341",marshall_islands:"unicode/1f1f2-1f1ed",martial_arts_uniform:"unicode/1f94b",martinique:"unicode/1f1f2-1f1f6",mask:"unicode/1f637",massage:"unicode/1f486",massage_man:"unicode/1f486-2642",massage_woman:"unicode/1f486-2640",mate:"unicode/1f9c9",mauritania:"unicode/1f1f2-1f1f7",mauritius:"unicode/1f1f2-1f1fa",mayotte:"unicode/1f1fe-1f1f9",meat_on_bone:"unicode/1f356",mechanic:"unicode/1f9d1-1f527",mechanical_arm:"unicode/1f9be",mechanical_leg:"unicode/1f9bf",medal_military:"unicode/1f396",medal_sports:"unicode/1f3c5",medical_symbol:"unicode/2695",mega:"unicode/1f4e3",melon:"unicode/1f348",memo:"unicode/1f4dd",men_wrestling:"unicode/1f93c-2642",menorah:"unicode/1f54e",mens:"unicode/1f6b9",mermaid:"unicode/1f9dc-2640",merman:"unicode/1f9dc-2642",merperson:"unicode/1f9dc",metal:"unicode/1f918",metro:"unicode/1f687",mexico:"unicode/1f1f2-1f1fd",microbe:"unicode/1f9a0",micronesia:"unicode/1f1eb-1f1f2",microphone:"unicode/1f3a4",microscope:"unicode/1f52c",middle_finger:"unicode/1f595",milk_glass:"unicode/1f95b",milky_way:"unicode/1f30c",minibus:"unicode/1f690",minidisc:"unicode/1f4bd",mobile_phone_off:"unicode/1f4f4",moldova:"unicode/1f1f2-1f1e9",monaco:"unicode/1f1f2-1f1e8",money_mouth_face:"unicode/1f911",money_with_wings:"unicode/1f4b8",moneybag:"unicode/1f4b0",mongolia:"unicode/1f1f2-1f1f3",monkey:"unicode/1f412",monkey_face:"unicode/1f435",monocle_face:"unicode/1f9d0",monorail:"unicode/1f69d",montenegro:"unicode/1f1f2-1f1ea",montserrat:"unicode/1f1f2-1f1f8",moon:"unicode/1f314",moon_cake:"unicode/1f96e",morocco:"unicode/1f1f2-1f1e6",mortar_board:"unicode/1f393",mosque:"unicode/1f54c",mosquito:"unicode/1f99f",motor_boat:"unicode/1f6e5",motor_scooter:"unicode/1f6f5",motorcycle:"unicode/1f3cd",motorized_wheelchair:"unicode/1f9bc",motorway:"unicode/1f6e3",mount_fuji:"unicode/1f5fb",mountain:"unicode/26f0",mountain_bicyclist:"unicode/1f6b5",mountain_biking_man:"unicode/1f6b5-2642",mountain_biking_woman:"unicode/1f6b5-2640",mountain_cableway:"unicode/1f6a0",mountain_railway:"unicode/1f69e",mountain_snow:"unicode/1f3d4",mouse:"unicode/1f42d",mouse2:"unicode/1f401",movie_camera:"unicode/1f3a5",moyai:"unicode/1f5ff",mozambique:"unicode/1f1f2-1f1ff",mrs_claus:"unicode/1f936",muscle:"unicode/1f4aa",mushroom:"unicode/1f344",musical_keyboard:"unicode/1f3b9",musical_note:"unicode/1f3b5",musical_score:"unicode/1f3bc",mute:"unicode/1f507",myanmar:"unicode/1f1f2-1f1f2",nail_care:"unicode/1f485",name_badge:"unicode/1f4db",namibia:"unicode/1f1f3-1f1e6",national_park:"unicode/1f3de",nauru:"unicode/1f1f3-1f1f7",nauseated_face:"unicode/1f922",nazar_amulet:"unicode/1f9ff",neckbeard:"neckbeard",necktie:"unicode/1f454",negative_squared_cross_mark:"unicode/274e",nepal:"unicode/1f1f3-1f1f5",nerd_face:"unicode/1f913",netherlands:"unicode/1f1f3-1f1f1",neutral_face:"unicode/1f610",new:"unicode/1f195",new_caledonia:"unicode/1f1f3-1f1e8",new_moon:"unicode/1f311",new_moon_with_face:"unicode/1f31a",new_zealand:"unicode/1f1f3-1f1ff",newspaper:"unicode/1f4f0",newspaper_roll:"unicode/1f5de",next_track_button:"unicode/23ed",ng:"unicode/1f196",ng_man:"unicode/1f645-2642",ng_woman:"unicode/1f645-2640",nicaragua:"unicode/1f1f3-1f1ee",niger:"unicode/1f1f3-1f1ea",nigeria:"unicode/1f1f3-1f1ec",night_with_stars:"unicode/1f303",nine:"unicode/0039-20e3",niue:"unicode/1f1f3-1f1fa",no_bell:"unicode/1f515",no_bicycles:"unicode/1f6b3",no_entry:"unicode/26d4",no_entry_sign:"unicode/1f6ab",no_good:"unicode/1f645",no_good_man:"unicode/1f645-2642",no_good_woman:"unicode/1f645-2640",no_mobile_phones:"unicode/1f4f5",no_mouth:"unicode/1f636",no_pedestrians:"unicode/1f6b7",no_smoking:"unicode/1f6ad","non-potable_water":"unicode/1f6b1",norfolk_island:"unicode/1f1f3-1f1eb",north_korea:"unicode/1f1f0-1f1f5",northern_mariana_islands:"unicode/1f1f2-1f1f5",norway:"unicode/1f1f3-1f1f4",nose:"unicode/1f443",notebook:"unicode/1f4d3",notebook_with_decorative_cover:"unicode/1f4d4",notes:"unicode/1f3b6",nut_and_bolt:"unicode/1f529",o:"unicode/2b55",o2:"unicode/1f17e",ocean:"unicode/1f30a",octocat:"octocat",octopus:"unicode/1f419",oden:"unicode/1f362",office:"unicode/1f3e2",office_worker:"unicode/1f9d1-1f4bc",oil_drum:"unicode/1f6e2",ok:"unicode/1f197",ok_hand:"unicode/1f44c",ok_man:"unicode/1f646-2642",ok_person:"unicode/1f646",ok_woman:"unicode/1f646-2640",old_key:"unicode/1f5dd",older_adult:"unicode/1f9d3",older_man:"unicode/1f474",older_woman:"unicode/1f475",om:"unicode/1f549",oman:"unicode/1f1f4-1f1f2",on:"unicode/1f51b",oncoming_automobile:"unicode/1f698",oncoming_bus:"unicode/1f68d",oncoming_police_car:"unicode/1f694",oncoming_taxi:"unicode/1f696",one:"unicode/0031-20e3",one_piece_swimsuit:"unicode/1fa71",onion:"unicode/1f9c5",open_book:"unicode/1f4d6",open_file_folder:"unicode/1f4c2",open_hands:"unicode/1f450",open_mouth:"unicode/1f62e",open_umbrella:"unicode/2602",ophiuchus:"unicode/26ce",orange:"unicode/1f34a",orange_book:"unicode/1f4d9",orange_circle:"unicode/1f7e0",orange_heart:"unicode/1f9e1",orange_square:"unicode/1f7e7",orangutan:"unicode/1f9a7",orthodox_cross:"unicode/2626",otter:"unicode/1f9a6",outbox_tray:"unicode/1f4e4",owl:"unicode/1f989",ox:"unicode/1f402",oyster:"unicode/1f9aa",package:"unicode/1f4e6",page_facing_up:"unicode/1f4c4",page_with_curl:"unicode/1f4c3",pager:"unicode/1f4df",paintbrush:"unicode/1f58c",pakistan:"unicode/1f1f5-1f1f0",palau:"unicode/1f1f5-1f1fc",palestinian_territories:"unicode/1f1f5-1f1f8",palm_tree:"unicode/1f334",palms_up_together:"unicode/1f932",panama:"unicode/1f1f5-1f1e6",pancakes:"unicode/1f95e",panda_face:"unicode/1f43c",paperclip:"unicode/1f4ce",paperclips:"unicode/1f587",papua_new_guinea:"unicode/1f1f5-1f1ec",parachute:"unicode/1fa82",paraguay:"unicode/1f1f5-1f1fe",parasol_on_ground:"unicode/26f1",parking:"unicode/1f17f",parrot:"unicode/1f99c",part_alternation_mark:"unicode/303d",partly_sunny:"unicode/26c5",partying_face:"unicode/1f973",passenger_ship:"unicode/1f6f3",passport_control:"unicode/1f6c2",pause_button:"unicode/23f8",paw_prints:"unicode/1f43e",peace_symbol:"unicode/262e",peach:"unicode/1f351",peacock:"unicode/1f99a",peanuts:"unicode/1f95c",pear:"unicode/1f350",pen:"unicode/1f58a",pencil:"unicode/1f4dd",pencil2:"unicode/270f",penguin:"unicode/1f427",pensive:"unicode/1f614",people_holding_hands:"unicode/1f9d1-1f91d-1f9d1",performing_arts:"unicode/1f3ad",persevere:"unicode/1f623",person_bald:"unicode/1f9d1-1f9b2",person_curly_hair:"unicode/1f9d1-1f9b1",person_fencing:"unicode/1f93a",person_in_manual_wheelchair:"unicode/1f9d1-1f9bd",person_in_motorized_wheelchair:"unicode/1f9d1-1f9bc",person_red_hair:"unicode/1f9d1-1f9b0",person_white_hair:"unicode/1f9d1-1f9b3",person_with_probing_cane:"unicode/1f9d1-1f9af",person_with_turban:"unicode/1f473",peru:"unicode/1f1f5-1f1ea",petri_dish:"unicode/1f9eb",philippines:"unicode/1f1f5-1f1ed",phone:"unicode/260e",pick:"unicode/26cf",pie:"unicode/1f967",pig:"unicode/1f437",pig2:"unicode/1f416",pig_nose:"unicode/1f43d",pill:"unicode/1f48a",pilot:"unicode/1f9d1-2708",pinching_hand:"unicode/1f90f",pineapple:"unicode/1f34d",ping_pong:"unicode/1f3d3",pirate_flag:"unicode/1f3f4-2620",pisces:"unicode/2653",pitcairn_islands:"unicode/1f1f5-1f1f3",pizza:"unicode/1f355",place_of_worship:"unicode/1f6d0",plate_with_cutlery:"unicode/1f37d",play_or_pause_button:"unicode/23ef",pleading_face:"unicode/1f97a",point_down:"unicode/1f447",point_left:"unicode/1f448",point_right:"unicode/1f449",point_up:"unicode/261d",point_up_2:"unicode/1f446",poland:"unicode/1f1f5-1f1f1",police_car:"unicode/1f693",police_officer:"unicode/1f46e",policeman:"unicode/1f46e-2642",policewoman:"unicode/1f46e-2640",poodle:"unicode/1f429",poop:"unicode/1f4a9",popcorn:"unicode/1f37f",portugal:"unicode/1f1f5-1f1f9",post_office:"unicode/1f3e3",postal_horn:"unicode/1f4ef",postbox:"unicode/1f4ee",potable_water:"unicode/1f6b0",potato:"unicode/1f954",pouch:"unicode/1f45d",poultry_leg:"unicode/1f357",pound:"unicode/1f4b7",pout:"unicode/1f621",pouting_cat:"unicode/1f63e",pouting_face:"unicode/1f64e",pouting_man:"unicode/1f64e-2642",pouting_woman:"unicode/1f64e-2640",pray:"unicode/1f64f",prayer_beads:"unicode/1f4ff",pregnant_woman:"unicode/1f930",pretzel:"unicode/1f968",previous_track_button:"unicode/23ee",prince:"unicode/1f934",princess:"unicode/1f478",printer:"unicode/1f5a8",probing_cane:"unicode/1f9af",puerto_rico:"unicode/1f1f5-1f1f7",punch:"unicode/1f44a",purple_circle:"unicode/1f7e3",purple_heart:"unicode/1f49c",purple_square:"unicode/1f7ea",purse:"unicode/1f45b",pushpin:"unicode/1f4cc",put_litter_in_its_place:"unicode/1f6ae",qatar:"unicode/1f1f6-1f1e6",question:"unicode/2753",rabbit:"unicode/1f430",rabbit2:"unicode/1f407",raccoon:"unicode/1f99d",racehorse:"unicode/1f40e",racing_car:"unicode/1f3ce",radio:"unicode/1f4fb",radio_button:"unicode/1f518",radioactive:"unicode/2622",rage:"unicode/1f621",rage1:"rage1",rage2:"rage2",rage3:"rage3",rage4:"rage4",railway_car:"unicode/1f683",railway_track:"unicode/1f6e4",rainbow:"unicode/1f308",rainbow_flag:"unicode/1f3f3-1f308",raised_back_of_hand:"unicode/1f91a",raised_eyebrow:"unicode/1f928",raised_hand:"unicode/270b",raised_hand_with_fingers_splayed:"unicode/1f590",raised_hands:"unicode/1f64c",raising_hand:"unicode/1f64b",raising_hand_man:"unicode/1f64b-2642",raising_hand_woman:"unicode/1f64b-2640",ram:"unicode/1f40f",ramen:"unicode/1f35c",rat:"unicode/1f400",razor:"unicode/1fa92",receipt:"unicode/1f9fe",record_button:"unicode/23fa",recycle:"unicode/267b",red_car:"unicode/1f697",red_circle:"unicode/1f534",red_envelope:"unicode/1f9e7",red_haired_man:"unicode/1f468-1f9b0",red_haired_woman:"unicode/1f469-1f9b0",red_square:"unicode/1f7e5",registered:"unicode/00ae",relaxed:"unicode/263a",relieved:"unicode/1f60c",reminder_ribbon:"unicode/1f397",repeat:"unicode/1f501",repeat_one:"unicode/1f502",rescue_worker_helmet:"unicode/26d1",restroom:"unicode/1f6bb",reunion:"unicode/1f1f7-1f1ea",revolving_hearts:"unicode/1f49e",rewind:"unicode/23ea",rhinoceros:"unicode/1f98f",ribbon:"unicode/1f380",rice:"unicode/1f35a",rice_ball:"unicode/1f359",rice_cracker:"unicode/1f358",rice_scene:"unicode/1f391",right_anger_bubble:"unicode/1f5ef",ring:"unicode/1f48d",ringed_planet:"unicode/1fa90",robot:"unicode/1f916",rocket:"unicode/1f680",rofl:"unicode/1f923",roll_eyes:"unicode/1f644",roll_of_paper:"unicode/1f9fb",roller_coaster:"unicode/1f3a2",romania:"unicode/1f1f7-1f1f4",rooster:"unicode/1f413",rose:"unicode/1f339",rosette:"unicode/1f3f5",rotating_light:"unicode/1f6a8",round_pushpin:"unicode/1f4cd",rowboat:"unicode/1f6a3",rowing_man:"unicode/1f6a3-2642",rowing_woman:"unicode/1f6a3-2640",ru:"unicode/1f1f7-1f1fa",rugby_football:"unicode/1f3c9",runner:"unicode/1f3c3",running:"unicode/1f3c3",running_man:"unicode/1f3c3-2642",running_shirt_with_sash:"unicode/1f3bd",running_woman:"unicode/1f3c3-2640",rwanda:"unicode/1f1f7-1f1fc",sa:"unicode/1f202",safety_pin:"unicode/1f9f7",safety_vest:"unicode/1f9ba",sagittarius:"unicode/2650",sailboat:"unicode/26f5",sake:"unicode/1f376",salt:"unicode/1f9c2",samoa:"unicode/1f1fc-1f1f8",san_marino:"unicode/1f1f8-1f1f2",sandal:"unicode/1f461",sandwich:"unicode/1f96a",santa:"unicode/1f385",sao_tome_principe:"unicode/1f1f8-1f1f9",sari:"unicode/1f97b",sassy_man:"unicode/1f481-2642",sassy_woman:"unicode/1f481-2640",satellite:"unicode/1f4e1",satisfied:"unicode/1f606",saudi_arabia:"unicode/1f1f8-1f1e6",sauna_man:"unicode/1f9d6-2642",sauna_person:"unicode/1f9d6",sauna_woman:"unicode/1f9d6-2640",sauropod:"unicode/1f995",saxophone:"unicode/1f3b7",scarf:"unicode/1f9e3",school:"unicode/1f3eb",school_satchel:"unicode/1f392",scientist:"unicode/1f9d1-1f52c",scissors:"unicode/2702",scorpion:"unicode/1f982",scorpius:"unicode/264f",scotland:"unicode/1f3f4-e0067-e0062-e0073-e0063-e0074-e007f",scream:"unicode/1f631",scream_cat:"unicode/1f640",scroll:"unicode/1f4dc",seat:"unicode/1f4ba",secret:"unicode/3299",see_no_evil:"unicode/1f648",seedling:"unicode/1f331",selfie:"unicode/1f933",senegal:"unicode/1f1f8-1f1f3",serbia:"unicode/1f1f7-1f1f8",service_dog:"unicode/1f415-1f9ba",seven:"unicode/0037-20e3",seychelles:"unicode/1f1f8-1f1e8",shallow_pan_of_food:"unicode/1f958",shamrock:"unicode/2618",shark:"unicode/1f988",shaved_ice:"unicode/1f367",sheep:"unicode/1f411",shell:"unicode/1f41a",shield:"unicode/1f6e1",shinto_shrine:"unicode/26e9",ship:"unicode/1f6a2",shipit:"shipit",shirt:"unicode/1f455",shit:"unicode/1f4a9",shoe:"unicode/1f45e",shopping:"unicode/1f6cd",shopping_cart:"unicode/1f6d2",shorts:"unicode/1fa73",shower:"unicode/1f6bf",shrimp:"unicode/1f990",shrug:"unicode/1f937",shushing_face:"unicode/1f92b",sierra_leone:"unicode/1f1f8-1f1f1",signal_strength:"unicode/1f4f6",singapore:"unicode/1f1f8-1f1ec",singer:"unicode/1f9d1-1f3a4",sint_maarten:"unicode/1f1f8-1f1fd",six:"unicode/0036-20e3",six_pointed_star:"unicode/1f52f",skateboard:"unicode/1f6f9",ski:"unicode/1f3bf",skier:"unicode/26f7",skull:"unicode/1f480",skull_and_crossbones:"unicode/2620",skunk:"unicode/1f9a8",sled:"unicode/1f6f7",sleeping:"unicode/1f634",sleeping_bed:"unicode/1f6cc",sleepy:"unicode/1f62a",slightly_frowning_face:"unicode/1f641",slightly_smiling_face:"unicode/1f642",slot_machine:"unicode/1f3b0",sloth:"unicode/1f9a5",slovakia:"unicode/1f1f8-1f1f0",slovenia:"unicode/1f1f8-1f1ee",small_airplane:"unicode/1f6e9",small_blue_diamond:"unicode/1f539",small_orange_diamond:"unicode/1f538",small_red_triangle:"unicode/1f53a",small_red_triangle_down:"unicode/1f53b",smile:"unicode/1f604",smile_cat:"unicode/1f638",smiley:"unicode/1f603",smiley_cat:"unicode/1f63a",smiling_face_with_three_hearts:"unicode/1f970",smiling_imp:"unicode/1f608",smirk:"unicode/1f60f",smirk_cat:"unicode/1f63c",smoking:"unicode/1f6ac",snail:"unicode/1f40c",snake:"unicode/1f40d",sneezing_face:"unicode/1f927",snowboarder:"unicode/1f3c2",snowflake:"unicode/2744",snowman:"unicode/26c4",snowman_with_snow:"unicode/2603",soap:"unicode/1f9fc",sob:"unicode/1f62d",soccer:"unicode/26bd",socks:"unicode/1f9e6",softball:"unicode/1f94e",solomon_islands:"unicode/1f1f8-1f1e7",somalia:"unicode/1f1f8-1f1f4",soon:"unicode/1f51c",sos:"unicode/1f198",sound:"unicode/1f509",south_africa:"unicode/1f1ff-1f1e6",south_georgia_south_sandwich_islands:"unicode/1f1ec-1f1f8",south_sudan:"unicode/1f1f8-1f1f8",space_invader:"unicode/1f47e",spades:"unicode/2660",spaghetti:"unicode/1f35d",sparkle:"unicode/2747",sparkler:"unicode/1f387",sparkles:"unicode/2728",sparkling_heart:"unicode/1f496",speak_no_evil:"unicode/1f64a",speaker:"unicode/1f508",speaking_head:"unicode/1f5e3",speech_balloon:"unicode/1f4ac",speedboat:"unicode/1f6a4",spider:"unicode/1f577",spider_web:"unicode/1f578",spiral_calendar:"unicode/1f5d3",spiral_notepad:"unicode/1f5d2",sponge:"unicode/1f9fd",spoon:"unicode/1f944",squid:"unicode/1f991",sri_lanka:"unicode/1f1f1-1f1f0",st_barthelemy:"unicode/1f1e7-1f1f1",st_helena:"unicode/1f1f8-1f1ed",st_kitts_nevis:"unicode/1f1f0-1f1f3",st_lucia:"unicode/1f1f1-1f1e8",st_martin:"unicode/1f1f2-1f1eb",st_pierre_miquelon:"unicode/1f1f5-1f1f2",st_vincent_grenadines:"unicode/1f1fb-1f1e8",stadium:"unicode/1f3df",standing_man:"unicode/1f9cd-2642",standing_person:"unicode/1f9cd",standing_woman:"unicode/1f9cd-2640",star:"unicode/2b50",star2:"unicode/1f31f",star_and_crescent:"unicode/262a",star_of_david:"unicode/2721",star_struck:"unicode/1f929",stars:"unicode/1f320",station:"unicode/1f689",statue_of_liberty:"unicode/1f5fd",steam_locomotive:"unicode/1f682",stethoscope:"unicode/1fa7a",stew:"unicode/1f372",stop_button:"unicode/23f9",stop_sign:"unicode/1f6d1",stopwatch:"unicode/23f1",straight_ruler:"unicode/1f4cf",strawberry:"unicode/1f353",stuck_out_tongue:"unicode/1f61b",stuck_out_tongue_closed_eyes:"unicode/1f61d",stuck_out_tongue_winking_eye:"unicode/1f61c",student:"unicode/1f9d1-1f393",studio_microphone:"unicode/1f399",stuffed_flatbread:"unicode/1f959",sudan:"unicode/1f1f8-1f1e9",sun_behind_large_cloud:"unicode/1f325",sun_behind_rain_cloud:"unicode/1f326",sun_behind_small_cloud:"unicode/1f324",sun_with_face:"unicode/1f31e",sunflower:"unicode/1f33b",sunglasses:"unicode/1f60e",sunny:"unicode/2600",sunrise:"unicode/1f305",sunrise_over_mountains:"unicode/1f304",superhero:"unicode/1f9b8",superhero_man:"unicode/1f9b8-2642",superhero_woman:"unicode/1f9b8-2640",supervillain:"unicode/1f9b9",supervillain_man:"unicode/1f9b9-2642",supervillain_woman:"unicode/1f9b9-2640",surfer:"unicode/1f3c4",surfing_man:"unicode/1f3c4-2642",surfing_woman:"unicode/1f3c4-2640",suriname:"unicode/1f1f8-1f1f7",sushi:"unicode/1f363",suspect:"suspect",suspension_railway:"unicode/1f69f",svalbard_jan_mayen:"unicode/1f1f8-1f1ef",swan:"unicode/1f9a2",swaziland:"unicode/1f1f8-1f1ff",sweat:"unicode/1f613",sweat_drops:"unicode/1f4a6",sweat_smile:"unicode/1f605",sweden:"unicode/1f1f8-1f1ea",sweet_potato:"unicode/1f360",swim_brief:"unicode/1fa72",swimmer:"unicode/1f3ca",swimming_man:"unicode/1f3ca-2642",swimming_woman:"unicode/1f3ca-2640",switzerland:"unicode/1f1e8-1f1ed",symbols:"unicode/1f523",synagogue:"unicode/1f54d",syria:"unicode/1f1f8-1f1fe",syringe:"unicode/1f489","t-rex":"unicode/1f996",taco:"unicode/1f32e",tada:"unicode/1f389",taiwan:"unicode/1f1f9-1f1fc",tajikistan:"unicode/1f1f9-1f1ef",takeout_box:"unicode/1f961",tanabata_tree:"unicode/1f38b",tangerine:"unicode/1f34a",tanzania:"unicode/1f1f9-1f1ff",taurus:"unicode/2649",taxi:"unicode/1f695",tea:"unicode/1f375",teacher:"unicode/1f9d1-1f3eb",technologist:"unicode/1f9d1-1f4bb",teddy_bear:"unicode/1f9f8",telephone:"unicode/260e",telephone_receiver:"unicode/1f4de",telescope:"unicode/1f52d",tennis:"unicode/1f3be",tent:"unicode/26fa",test_tube:"unicode/1f9ea",thailand:"unicode/1f1f9-1f1ed",thermometer:"unicode/1f321",thinking:"unicode/1f914",thought_balloon:"unicode/1f4ad",thread:"unicode/1f9f5",three:"unicode/0033-20e3",thumbsdown:"unicode/1f44e",thumbsup:"unicode/1f44d",ticket:"unicode/1f3ab",tickets:"unicode/1f39f",tiger:"unicode/1f42f",tiger2:"unicode/1f405",timer_clock:"unicode/23f2",timor_leste:"unicode/1f1f9-1f1f1",tipping_hand_man:"unicode/1f481-2642",tipping_hand_person:"unicode/1f481",tipping_hand_woman:"unicode/1f481-2640",tired_face:"unicode/1f62b",tm:"unicode/2122",togo:"unicode/1f1f9-1f1ec",toilet:"unicode/1f6bd",tokelau:"unicode/1f1f9-1f1f0",tokyo_tower:"unicode/1f5fc",tomato:"unicode/1f345",tonga:"unicode/1f1f9-1f1f4",tongue:"unicode/1f445",toolbox:"unicode/1f9f0",tooth:"unicode/1f9b7",top:"unicode/1f51d",tophat:"unicode/1f3a9",tornado:"unicode/1f32a",tr:"unicode/1f1f9-1f1f7",trackball:"unicode/1f5b2",tractor:"unicode/1f69c",traffic_light:"unicode/1f6a5",train:"unicode/1f68b",train2:"unicode/1f686",tram:"unicode/1f68a",triangular_flag_on_post:"unicode/1f6a9",triangular_ruler:"unicode/1f4d0",trident:"unicode/1f531",trinidad_tobago:"unicode/1f1f9-1f1f9",tristan_da_cunha:"unicode/1f1f9-1f1e6",triumph:"unicode/1f624",trolleybus:"unicode/1f68e",trollface:"trollface",trophy:"unicode/1f3c6",tropical_drink:"unicode/1f379",tropical_fish:"unicode/1f420",truck:"unicode/1f69a",trumpet:"unicode/1f3ba",tshirt:"unicode/1f455",tulip:"unicode/1f337",tumbler_glass:"unicode/1f943",tunisia:"unicode/1f1f9-1f1f3",turkey:"unicode/1f983",turkmenistan:"unicode/1f1f9-1f1f2",turks_caicos_islands:"unicode/1f1f9-1f1e8",turtle:"unicode/1f422",tuvalu:"unicode/1f1f9-1f1fb",tv:"unicode/1f4fa",twisted_rightwards_arrows:"unicode/1f500",two:"unicode/0032-20e3",two_hearts:"unicode/1f495",two_men_holding_hands:"unicode/1f46c",two_women_holding_hands:"unicode/1f46d",u5272:"unicode/1f239",u5408:"unicode/1f234",u55b6:"unicode/1f23a",u6307:"unicode/1f22f",u6708:"unicode/1f237",u6709:"unicode/1f236",u6e80:"unicode/1f235",u7121:"unicode/1f21a",u7533:"unicode/1f238",u7981:"unicode/1f232",u7a7a:"unicode/1f233",uganda:"unicode/1f1fa-1f1ec",uk:"unicode/1f1ec-1f1e7",ukraine:"unicode/1f1fa-1f1e6",umbrella:"unicode/2614",unamused:"unicode/1f612",underage:"unicode/1f51e",unicorn:"unicode/1f984",united_arab_emirates:"unicode/1f1e6-1f1ea",united_nations:"unicode/1f1fa-1f1f3",unlock:"unicode/1f513",up:"unicode/1f199",upside_down_face:"unicode/1f643",uruguay:"unicode/1f1fa-1f1fe",us:"unicode/1f1fa-1f1f8",us_outlying_islands:"unicode/1f1fa-1f1f2",us_virgin_islands:"unicode/1f1fb-1f1ee",uzbekistan:"unicode/1f1fa-1f1ff",v:"unicode/270c",vampire:"unicode/1f9db",vampire_man:"unicode/1f9db-2642",vampire_woman:"unicode/1f9db-2640",vanuatu:"unicode/1f1fb-1f1fa",vatican_city:"unicode/1f1fb-1f1e6",venezuela:"unicode/1f1fb-1f1ea",vertical_traffic_light:"unicode/1f6a6",vhs:"unicode/1f4fc",vibration_mode:"unicode/1f4f3",video_camera:"unicode/1f4f9",video_game:"unicode/1f3ae",vietnam:"unicode/1f1fb-1f1f3",violin:"unicode/1f3bb",virgo:"unicode/264d",volcano:"unicode/1f30b",volleyball:"unicode/1f3d0",vomiting_face:"unicode/1f92e",vs:"unicode/1f19a",vulcan_salute:"unicode/1f596",waffle:"unicode/1f9c7",wales:"unicode/1f3f4-e0067-e0062-e0077-e006c-e0073-e007f",walking:"unicode/1f6b6",walking_man:"unicode/1f6b6-2642",walking_woman:"unicode/1f6b6-2640",wallis_futuna:"unicode/1f1fc-1f1eb",waning_crescent_moon:"unicode/1f318",waning_gibbous_moon:"unicode/1f316",warning:"unicode/26a0",wastebasket:"unicode/1f5d1",watch:"unicode/231a",water_buffalo:"unicode/1f403",water_polo:"unicode/1f93d",watermelon:"unicode/1f349",wave:"unicode/1f44b",wavy_dash:"unicode/3030",waxing_crescent_moon:"unicode/1f312",waxing_gibbous_moon:"unicode/1f314",wc:"unicode/1f6be",weary:"unicode/1f629",wedding:"unicode/1f492",weight_lifting:"unicode/1f3cb",weight_lifting_man:"unicode/1f3cb-2642",weight_lifting_woman:"unicode/1f3cb-2640",western_sahara:"unicode/1f1ea-1f1ed",whale:"unicode/1f433",whale2:"unicode/1f40b",wheel_of_dharma:"unicode/2638",wheelchair:"unicode/267f",white_check_mark:"unicode/2705",white_circle:"unicode/26aa",white_flag:"unicode/1f3f3",white_flower:"unicode/1f4ae",white_haired_man:"unicode/1f468-1f9b3",white_haired_woman:"unicode/1f469-1f9b3",white_heart:"unicode/1f90d",white_large_square:"unicode/2b1c",white_medium_small_square:"unicode/25fd",white_medium_square:"unicode/25fb",white_small_square:"unicode/25ab",white_square_button:"unicode/1f533",wilted_flower:"unicode/1f940",wind_chime:"unicode/1f390",wind_face:"unicode/1f32c",wine_glass:"unicode/1f377",wink:"unicode/1f609",wolf:"unicode/1f43a",woman:"unicode/1f469",woman_artist:"unicode/1f469-1f3a8",woman_astronaut:"unicode/1f469-1f680",woman_cartwheeling:"unicode/1f938-2640",woman_cook:"unicode/1f469-1f373",woman_dancing:"unicode/1f483",woman_facepalming:"unicode/1f926-2640",woman_factory_worker:"unicode/1f469-1f3ed",woman_farmer:"unicode/1f469-1f33e",woman_firefighter:"unicode/1f469-1f692",woman_health_worker:"unicode/1f469-2695",woman_in_manual_wheelchair:"unicode/1f469-1f9bd",woman_in_motorized_wheelchair:"unicode/1f469-1f9bc",woman_judge:"unicode/1f469-2696",woman_juggling:"unicode/1f939-2640",woman_mechanic:"unicode/1f469-1f527",woman_office_worker:"unicode/1f469-1f4bc",woman_pilot:"unicode/1f469-2708",woman_playing_handball:"unicode/1f93e-2640",woman_playing_water_polo:"unicode/1f93d-2640",woman_scientist:"unicode/1f469-1f52c",woman_shrugging:"unicode/1f937-2640",woman_singer:"unicode/1f469-1f3a4",woman_student:"unicode/1f469-1f393",woman_teacher:"unicode/1f469-1f3eb",woman_technologist:"unicode/1f469-1f4bb",woman_with_headscarf:"unicode/1f9d5",woman_with_probing_cane:"unicode/1f469-1f9af",woman_with_turban:"unicode/1f473-2640",womans_clothes:"unicode/1f45a",womans_hat:"unicode/1f452",women_wrestling:"unicode/1f93c-2640",womens:"unicode/1f6ba",woozy_face:"unicode/1f974",world_map:"unicode/1f5fa",worried:"unicode/1f61f",wrench:"unicode/1f527",wrestling:"unicode/1f93c",writing_hand:"unicode/270d",x:"unicode/274c",yarn:"unicode/1f9f6",yawning_face:"unicode/1f971",yellow_circle:"unicode/1f7e1",yellow_heart:"unicode/1f49b",yellow_square:"unicode/1f7e8",yemen:"unicode/1f1fe-1f1ea",yen:"unicode/1f4b4",yin_yang:"unicode/262f",yo_yo:"unicode/1fa80",yum:"unicode/1f60b",zambia:"unicode/1f1ff-1f1f2",zany_face:"unicode/1f92a",zap:"unicode/26a1",zebra:"unicode/1f993",zero:"unicode/0030-20e3",zimbabwe:"unicode/1f1ff-1f1fc",zipper_mouth_face:"unicode/1f910",zombie:"unicode/1f9df",zombie_man:"unicode/1f9df-2642",zombie_woman:"unicode/1f9df-2640",zzz:"unicode/1f4a4"};window.emojify=function(e,n){return!1===o.hasOwnProperty(n)?e:'<img class="emoji" src="https://github.githubassets.com/images/icons/emoji/'+o[n]+'.png" alt="'+n+'" />'}}(); diff --git a/docs/_scripts/prism-c.min.js b/docs/_scripts/prism-c.min.js new file mode 100644 index 0000000..6a4fff5 --- /dev/null +++ b/docs/_scripts/prism-c.min.js @@ -0,0 +1 @@ +Prism.languages.c=Prism.languages.extend("clike",{comment:{pattern:/\/\/(?:[^\r\n\\]|\\(?:\r\n?|\n|(?![\r\n])))*|\/\*[\s\S]*?(?:\*\/|$)/,greedy:!0},"class-name":{pattern:/(\b(?:enum|struct)\s+(?:__attribute__\s*\(\([\s\S]*?\)\)\s*)?)\w+/,lookbehind:!0},keyword:/\b(?:__attribute__|_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/,function:/[a-z_]\w*(?=\s*\()/i,operator:/>>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/,number:/(?:\b0x(?:[\da-f]+\.?[\da-f]*|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?)[ful]*/i}),Prism.languages.insertBefore("c","string",{macro:{pattern:/(^\s*)#\s*[a-z]+(?:[^\r\n\\/]|\/(?!\*)|\/\*(?:[^*]|\*(?!\/))*\*\/|\\(?:\r\n|[\s\S]))*/im,lookbehind:!0,greedy:!0,alias:"property",inside:{string:[{pattern:/^(#\s*include\s*)<[^>]+>/,lookbehind:!0},Prism.languages.c.string],comment:Prism.languages.c.comment,directive:{pattern:/^(#\s*)[a-z]+/,lookbehind:!0,alias:"keyword"},"directive-hash":/^#/,punctuation:/##|\\(?=[\r\n])/,expression:{pattern:/\S[\s\S]*/,inside:Prism.languages.c}}},constant:/\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/}),delete Prism.languages.c.boolean; \ No newline at end of file diff --git a/docs/_scripts/search.min.js b/docs/_scripts/search.min.js new file mode 100644 index 0000000..f744744 --- /dev/null +++ b/docs/_scripts/search.min.js @@ -0,0 +1 @@ +!function(){var h={},f={EXPIRE_KEY:"docsify.search.expires",INDEX_KEY:"docsify.search.index"};function l(e){var n={"&":"&","<":"<",">":">",'"':""","'":"'"};return String(e).replace(/[&<>"']/g,function(e){return n[e]})}function p(e){return e.text||"table"!==e.type||(e.cells.unshift(e.header),e.text=e.cells.map(function(e){return e.join(" | ")}).join(" |\n ")),e.text}function u(r,e,i,o){void 0===e&&(e="");var s,n=window.marked.lexer(e),c=window.Docsify.slugify,d={};return n.forEach(function(e){if("heading"===e.type&&e.depth<=o){var n=function(e){void 0===e&&(e="");var a={};return{str:e=e&&e.replace(/^'/,"").replace(/'$/,"").replace(/(?:^|\s):([\w-]+:?)=?([\w-%]+)?/g,function(e,n,t){return-1===n.indexOf(":")?(a[n]=t&&t.replace(/"/g,"")||!0,""):e}).trim(),config:a}}(e.text),t=n.str,a=n.config;s=a.id?i.toURL(r,{id:c(a.id)}):i.toURL(r,{id:c(l(e.text))}),d[s]={slug:s,title:t,body:""}}else{if(!s)return;d[s]?d[s].body?(e.text=p(e),d[s].body+="\n"+(e.text||"")):(e.text=p(e),d[s].body=d[s].body?d[s].body+e.text:e.text):d[s]={slug:s,title:"",body:""}}}),c.clear(),d}function c(e){var r=[],i=[];Object.keys(h).forEach(function(n){i=i.concat(Object.keys(h[n]).map(function(e){return h[n][e]}))});var o=(e=e.trim()).split(/[\s\-,\\/]+/);1!==o.length&&(o=[].concat(e,o));function n(e){var n=i[e],s=0,c="",d=n.title&&n.title.trim(),p=n.body&&n.body.trim(),t=n.slug||"";if(d&&(o.forEach(function(e){var n,t=new RegExp(e.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&"),"gi"),a=-1;if(n=d?d.search(t):-1,a=p?p.search(t):-1,0<=n||0<=a){s+=0<=n?3:0<=a?2:0,a<0&&(a=0);var r,i=0;i=0==(r=a<11?0:a-10)?70:a+e.length+60,p&&i>p.length&&(i=p.length);var o="..."+l(p).substring(r,i).replace(t,function(e){return'<em class="search-keyword">'+e+"</em>"})+"...";c+=o}}),0<s)){var a={title:l(d),content:p?c:"",url:t,score:s};r.push(a)}}for(var t=0;t<i.length;t++)n(t);return r.sort(function(e,n){return n.score-e.score})}function i(t,a){var e="auto"===t.paths,n=e?function(r){var i=[];return Docsify.dom.findAll(".sidebar-nav a:not(.section-link):not([data-nosearch])").forEach(function(e){var n=e.href,t=e.getAttribute("href"),a=r.parse(n).path;a&&-1===i.indexOf(a)&&!Docsify.util.isAbsolutePath(t)&&i.push(a)}),i}(a.router):t.paths,r="";if(e&&t.pathNamespaces){var i=n[0];if(Array.isArray(t.pathNamespaces))r=t.pathNamespaces.find(function(e){return i.startsWith(e)})||r;else if(t.pathNamespaces instanceof RegExp){var o=i.match(t.pathNamespaces);o&&(r=o[0])}}var s=function(e){return e?f.EXPIRE_KEY+"/"+e:f.EXPIRE_KEY}(t.namespace)+r,c=function(e){return e?f.INDEX_KEY+"/"+e:f.INDEX_KEY}(t.namespace)+r,d=localStorage.getItem(s)<Date.now();if(h=JSON.parse(localStorage.getItem(c)),d)h={};else if(!e)return;var p=n.length,l=0;n.forEach(function(n){if(h[n])return l++;Docsify.get(a.router.getFile(n),!1,a.config.requestHeaders).then(function(e){h[n]=u(n,e,a.router,t.depth),p===++l&&function(e,n,t){localStorage.setItem(n,Date.now()+e),localStorage.setItem(t,JSON.stringify(h))}(t.maxAge,s,c)})})}var d,m="";function r(e){var n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,".results-panel"),a=Docsify.dom.find(n,".clear-button"),r=Docsify.dom.find(".sidebar-nav"),i=Docsify.dom.find(".app-name");if(!e)return t.classList.remove("show"),a.classList.remove("show"),t.innerHTML="",void(d.hideOtherSidebarContent&&(r.classList.remove("hide"),i.classList.remove("hide")));var o=c(e),s="";o.forEach(function(e){s+='<div class="matching-post">\n<a href="'+e.url+'">\n<h2>'+e.title+"</h2>\n<p>"+e.content+"</p>\n</a>\n</div>"}),t.classList.add("show"),a.classList.add("show"),t.innerHTML=s||'<p class="empty">'+m+"</p>",d.hideOtherSidebarContent&&(r.classList.add("hide"),i.classList.add("hide"))}function a(e){d=e}function o(e,n){var t=n.router.parse().query.s;a(e),Docsify.dom.style("\n.sidebar {\n padding-top: 0;\n}\n\n.search {\n margin-bottom: 20px;\n padding: 6px;\n border-bottom: 1px solid #eee;\n}\n\n.search .input-wrap {\n display: flex;\n align-items: center;\n}\n\n.search .results-panel {\n display: none;\n}\n\n.search .results-panel.show {\n display: block;\n}\n\n.search input {\n outline: none;\n border: none;\n width: 100%;\n padding: 0 7px;\n line-height: 36px;\n font-size: 14px;\n border: 1px solid transparent;\n}\n\n.search input:focus {\n box-shadow: 0 0 5px var(--theme-color, #42b983);\n border: 1px solid var(--theme-color, #42b983);\n}\n\n.search input::-webkit-search-decoration,\n.search input::-webkit-search-cancel-button,\n.search input {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n}\n.search .clear-button {\n cursor: pointer;\n width: 36px;\n text-align: right;\n display: none;\n}\n\n.search .clear-button.show {\n display: block;\n}\n\n.search .clear-button svg {\n transform: scale(.5);\n}\n\n.search h2 {\n font-size: 17px;\n margin: 10px 0;\n}\n\n.search a {\n text-decoration: none;\n color: inherit;\n}\n\n.search .matching-post {\n border-bottom: 1px solid #eee;\n}\n\n.search .matching-post:last-child {\n border-bottom: 0;\n}\n\n.search p {\n font-size: 14px;\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n}\n\n.search p.empty {\n text-align: center;\n}\n\n.app-name.hide, .sidebar-nav.hide {\n display: none;\n}"),function(e){void 0===e&&(e="");var n='<div class="input-wrap">\n <input type="search" value="'+e+'" aria-label="Search text" />\n <div class="clear-button">\n <svg width="26" height="24">\n <circle cx="12" cy="12" r="11" fill="#ccc" />\n <path stroke="white" stroke-width="2" d="M8.25,8.25,15.75,15.75" />\n <path stroke="white" stroke-width="2"d="M8.25,15.75,15.75,8.25" />\n </svg>\n </div>\n </div>\n <div class="results-panel"></div>\n </div>',t=Docsify.dom.create("div",n),a=Docsify.dom.find("aside");Docsify.dom.toggleClass(t,"search"),Docsify.dom.before(a,t)}(t),function(){var e,n=Docsify.dom.find("div.search"),t=Docsify.dom.find(n,"input"),a=Docsify.dom.find(n,".input-wrap");Docsify.dom.on(n,"click",function(e){return-1===["A","H2","P","EM"].indexOf(e.target.tagName)&&e.stopPropagation()}),Docsify.dom.on(t,"input",function(n){clearTimeout(e),e=setTimeout(function(e){return r(n.target.value.trim())},100)}),Docsify.dom.on(a,"click",function(e){"INPUT"!==e.target.tagName&&(t.value="",r())})}(),t&&setTimeout(function(e){return r(t)},500)}function s(e,n){a(e),function(e,n){var t=Docsify.dom.getNode('.search input[type="search"]');if(t)if("string"==typeof e)t.placeholder=e;else{var a=Object.keys(e).filter(function(e){return-1<n.indexOf(e)})[0];t.placeholder=e[a]}}(e.placeholder,n.route.path),function(e,n){if("string"==typeof e)m=e;else{var t=Object.keys(e).filter(function(e){return-1<n.indexOf(e)})[0];m=e[t]}}(e.noData,n.route.path)}var g={placeholder:"Type to search",noData:"No Results!",paths:"auto",depth:2,maxAge:864e5,hideOtherSidebarContent:!1,namespace:void 0,pathNamespaces:void 0};$docsify.plugins=[].concat(function(e,n){var t=Docsify.util,a=n.config.search||g;Array.isArray(a)?g.paths=a:"object"==typeof a&&(g.paths=Array.isArray(a.paths)?a.paths:"auto",g.maxAge=t.isPrimitive(a.maxAge)?a.maxAge:g.maxAge,g.placeholder=a.placeholder||g.placeholder,g.noData=a.noData||g.noData,g.depth=a.depth||g.depth,g.hideOtherSidebarContent=a.hideOtherSidebarContent||g.hideOtherSidebarContent,g.namespace=a.namespace||g.namespace,g.pathNamespaces=a.pathNamespaces||g.pathNamespaces);var r="auto"===g.paths;e.mounted(function(e){o(g,n),r||i(g,n)}),e.doneEach(function(e){s(g,n),r&&i(g,n)})},$docsify.plugins)}(); diff --git a/docs/_sidebar.md b/docs/_sidebar.md new file mode 100644 index 0000000..d5d693d --- /dev/null +++ b/docs/_sidebar.md @@ -0,0 +1,18 @@ +- Getting started + - [Quick Started](quick-started.md) + - Demo platforms + - [stm32f103ve](demo-stm32f103ve.md) + - [stm32f405rg](demo-stm32f405rg.md) + - [stm32f405rg-spi-flash](demo-stm32f405rg-spi-flash.md) +- Samples + - KV database + - [basic](sample-kvdb-basic.md) + - [string type](sample-kvdb-type-string.md) + - [blob type](sample-kvdb-type-blob.md) + - [KV traversal](sample-kvdb-traversal.md) + - Time series database + - [basic](sample-tsdb-basic.md) +- Development + - [porting](porting.md) + - [configuration](configuration.md) + - [API](api.md) diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..9f8fbf0 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,347 @@ +# API description + +## Blob + +### Construct blob object + +The process of constructing a blob object, its internal is the process of initializing and assigning values ​​to the blob structure, writing the incoming parameters into the blob structure, and returning it + +`fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len)` + +| Parameters | Description | +| --------- | ---------------------- | +| blob | blob initial object | +| value_buf | Buffer for storing data | +| buf_len | The size of the buffer | +| Back | The blob object after creation | + +### Read blob data + +Through the API of KVDB and TSDB, the blob object can be returned, and the storage address of the blob data is stored in the returned blob object. This API can read the blob data stored in the database and store it in `blob->buf`. + +`size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)` + +| Parameters | Description | +| ---- | -------------------------- | +| db | Database Objects | +| blob | blob object | +| Return | Length of blob data actually read | + +## KVDB + +### Initialize KVDB + +`fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *part_name, struct fdb_default_kv *default_kv, void *user_data)` + +| Parameters | Description | +| ---------- | -------------------------------------- ------------------ | +| db | Database Objects | +| name | Database name | +| part_name | Which partition in the FAL partition table to use | +| default_kv | The default KV collection, when the first initialization, the default KV will be written to the database | +| user_data | User-defined data, NULL if not available | +| Back | Error Code | + +### Control KVDB + +Through the command control word, the user can perform some control operations on the database + +`void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg)` + +| Parameters | Description | +| ---- | ---------- | +| db | Database Objects | +| cmd | Command control word | +| arg | Controlled parameters | +| Back | Error Code | + +The supported command control words are as follows: + +```C +#define FDB_KVDB_CTRL_SET_SEC_SIZE 0x0 /**< Set sector size */ +#define FDB_KVDB_CTRL_GET_SEC_SIZE 0x1 /**< Get sector size */ +#define FDB_KVDB_CTRL_SET_LOCK 0x2 /**< Set lock function */ +#define FDB_KVDB_CTRL_SET_UNLOCK 0x3 /**< Set unlock function */ +``` + +#### Sector size and block size + +The internal storage structure of FlashDB is composed of N sectors, and each formatting takes sector as the smallest unit. A sector is usually N times the size of the Flash block. For example, the block size of Nor Flash is generally 4096. + +By default, KVDB will use 1 times the block size as the sector size, that is, 4096. At this time, the KVDB cannot store a KV longer than 4096. If you want to save, for example, a KV with a length of 10K, you can use the control function to set the sector size to 12K or larger. + +### Set KV + +This method can be used to increase and modify KV. + +- **Add**: When there is no KV with this name in KVDB, the new operation will be performed; +- **Modify**: The KV name in the input parameter exists in the current KVDB, then the KV value is modified to the value in the input parameter; + +Get the corresponding value by KV's name. Support two interfaces + +#### Set blob type KV + +`fdb_err_t fdb_kv_set_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)` + +| Parameters | Description | +| ---- | ---------------------------- | +| db | Database Objects | +| key | KV name | +| blob | blob object, as the value of KV | +| Back | Error Code | + +Example: + +```C +struct fdb_blob blob; +int temp_data = 36; +/* Construct a blob object by fdb_blob_make as the value of "temp" KV */ +fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); +``` + +#### Set string type KV + +`fdb_err_t fdb_kv_set(fdb_kvdb_t db, const char *key, const char *value)` + +| Parameters | Description | +| ----- | ----------- | +| db | Database Objects | +| key | KV name | +| value | KV value | +| Back | Error Code | + +### Get KV + +#### Get blob type KV + +`size_t fdb_kv_get_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)` + +| Parameters | Description | +| ---- | ------------------------------------- | +| db | Database Objects | +| key | KV name | +| blob | Return the blob value of KV through the blob object | +| Back | Error Code | + +Example: + +```C +struct fdb_blob blob; +int temp_data = 0; +/* Construct a blob object to store the "temp" KV value data returned by get */ +fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); +/* If necessary, you can check whether blob.saved.len is greater than 0 to ensure that the get succeeds */ +if (blob.saved.len > 0) { + FDB_INFO("get the 'temp' value is: %d\n", temp_data); +} +``` + +#### Get KV object + +Unlike the `fdb_kv_get_blob` API, this API does not execute the reading of value data during the get process. The returned KV object stores the read KV attributes. This API is suitable for scenarios where the length of the value is uncertain, or the length of the value is too long, and it needs to be read in segments. + +`fdb_kv_t fdb_kv_get_obj(fdb_kvdb_t db, const char *key, fdb_kv_t kv)` + +| Parameters | Description | +| ---- | -------------------------------------------- ---------------- | +| db | Database Objects | +| key | KV name | +| kv | Through the KV object, return the attributes of the KV, and then use `fdb_kv_to_blob` to convert to a blob object, and then read the data | +| Back | Error Code | + +#### Get string type KV + +**Note**: + +- This function is not allowed to be used continuously, and `strdup` should be used when using it to ensure that the memory space of the string returned each time is independent; +- This function does not support reentrancy. The returned value is located in the internal buffer of the function. For security reasons, please lock it. + +`char *fdb_kv_get(fdb_kvdb_t db, const char *key)` + +| Parameters | Description | +| ---- | ----------------------------------- | +| db | Database Objects | +| key | KV name | +| Return | !=NULL: KV value; NULL: Get failed | + +### Delete KV + +`fdb_err_t fdb_kv_del(fdb_kvdb_t db, const char *key)` + +| Parameters | Description | +| ---- | ---------- | +| db | Database Objects | +| key | KV name | +| Back | Error Code | + +### Reset KVDB + +Reset the KV in KVDB to the initial default value + +`fdb_err_t fdb_kv_set_default(fdb_kvdb_t db)` + +| Parameters | Description | +| ---- | ---------- | +| db | Database Objects | +| Back | Error Code | + +### Print KV information in KVDB + +`void fdb_kv_print(fdb_kvdb_t db)` + +| Parameters | Description | +| ---- | ---------- | +| db | Database Objects | +| Back | Error Code | + +### Convert KV objects to blob objects + +`fdb_blob_t fdb_kv_to_blob(fdb_kv_t kv, fdb_blob_t blob)` + +| Parameters | Description | +| ---- | ------------------ | +| kv | KV object to be converted | +| blob | blob object before conversion | +| Return | Converted blob object | + +### Initialize KV iterator +`fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kv_iterator_t itr)` + +| Parameters | Description | +| ---- | -------------------- | +| itr | Iterator object to be initialized | +| Return | Iterator object after initialization | + +### Iteration KV + +Using this iterator API, all KVs in the entire KVDB can be traversed. + +> **Note**: Please initialize the iterator before use + +`bool fdb_kv_iterate(fdb_kvdb_t db, fdb_kv_iterator_t itr)` + +[Click to view sample](sample-kvdb-traversal.md) + +## TSDB + +### Initialize TSDB + +`fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name, fdb_get_time get_time, size_t max_len, void *user_data)` + +| Parameters | Description | +| --------- | ------------------------------- | +| db | Database Objects | +| name | Database name | +| part_name | Which partition in the FAL partition table to use | +| get_time | Function to get the current timestamp | +| max_len | Maximum length of each TSL | +| user_data | User-defined data, NULL if not available | +| Back | Error Code | + +### Control TSDB + +Through the command control word, the user can perform some control operations on the database + +`void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)` + +| Parameters | Description | +| ---- | ---------- | +| db | Database Objects | +| cmd | Command control word | +| arg | Controlled parameters | +| Back | Error Code | + +The supported command control words are as follows: + +```C +#define FDB_TSDB_CTRL_SET_SEC_SIZE 0x0 /**< Set sector size */ +#define FDB_TSDB_CTRL_GET_SEC_SIZE 0x1 /**< Get sector size */ +#define FDB_TSDB_CTRL_SET_LOCK 0x2 /**< Set lock function */ +#define FDB_TSDB_CTRL_SET_UNLOCK 0x3 /**< Set unlock function */ +#define FDB_TSDB_CTRL_SET_ROLLOVER 0x4 /**< Set whether to roll writing, default roll. When setting non-rolling, if the database is full, no more writes can be appended */ +#define FDB_TSDB_CTRL_GET_ROLLOVER 0x5 /**< Get whether to scroll to write */ +#define FDB_TSDB_CTRL_GET_LAST_TIME 0x6 /**< Get the timestamp when TSL was last appended */ +``` + +### Append TSL + +For TSDB, the process of adding TSL is the process of appending a new TSL to the end of TSDB + +`fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob)` + +| Parameters | Description | +| ---- | --------------------------- | +| db | Database Objects | +| blob | blob object, as TSL data | +| Back | Error Code | + +### Iterative TSL + +Traverse the entire TSDB and execute iterative callbacks + +`void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg)` + +| Parameters | Description | +| ------ | --------------------------------------- | +| db | Database Objects | +| cb | Callback function, which will be executed every time the TSL is traversed | +| cb_arg | Parameters of the callback function | +| Back | Error Code | + +### Iterate TSL by time period + +According to the time range, traverse the entire TSDB and execute iterative callbacks + +`void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg)` + +| Parameters | Description | +| ------ | --------------------------------------- | +| db | Database Objects | +| from | Start timestamp | +| to | End timestamp | +| cb | Callback function, which will be executed every time the TSL is traversed | +| cb_arg | Parameters of the callback function | +| Back | Error Code | + +### Query the number of TSL + +According to the incoming time period, query the number of TSLs that meet the state +`size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status)` + +| Parameters | Description | +| ------ | -------------- | +| db | Database Objects | +| from | Start timestamp | +| to | End timestamp | +| status | TSL status conditions | +| Back | Quantity | + +### Set TSL status + +`fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status)` + +| Parameters | Description | +| ------ | ------------ | +| db | Database Objects | +| tsl | TSL Object | +| status | TSL's new status | +| Back | Error Code | + +### Clear TSDB + +`void fdb_tsl_clean(fdb_tsdb_t db)` + +| Parameters | Description | +| ---- | ---------- | +| db | Database Objects | +| Back | Error Code | + +### Convert TSL objects to blob objects + +`fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob)` + +| Parameters | Description | +| ---- | ------------------ | +| tsl | TSL object to be converted | +| blob | blob object before conversion | +| Return | Converted blob object | \ No newline at end of file diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..fc30fc1 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,41 @@ +# Configuration + +When using FlashDB, you can configure its functions through `fdb_cfg.h`. The file template is located in the `inc` directory, or you can copy it in the specific demo project. Let's introduce the configuration details in detail below + +## FDB_USING_KVDB + +Enable KVDB feature + +### FDB_KV_AUTO_UPDATE + +Enable KV automatic upgrade function. After this function is enabled, `fdb_kvdb.ver_num` stores the version of the current database. If the version changes, it will automatically trigger an upgrade action and update the new default KV collection to the current database. + +## FDB_USING_TSDB + +Enable TSDB feature + +## FDB_WRITE_GRAN + +Flash write granularity, the unit is bit. Currently supports + +- 1: nor flash +- 8: stm32f2/f4 on-chip Flash +- 32: stm32f1 on-chip Flash + +If multiple Flash specifications are used in the database, for example: both nor flash and stm32f4 on-chip Flash, the maximum value is used as the configuration item, namely: 8 bit + +## FDB_BIG_ENDIAN + +MCU small-endian configuration, when the default is not configured, the system automatically uses the small-endian configuration + +## FDB_PRINT(...) + +The print function macro defines the configuration. When it is not configured by default, using `printf` as the print log is the output function. Users can also customize new print function macro definitions, for example: + +```C +#define FDB_PRINT(...) my_printf(__VA_ARGS__) +``` + +## FDB_DEBUG_ENABLE + +Enable debugging information output. When this configuration is closed, the system will not output logs for debugging. \ No newline at end of file diff --git a/docs/demo-stm32f103ve.md b/docs/demo-stm32f103ve.md new file mode 100644 index 0000000..f6e28b9 --- /dev/null +++ b/docs/demo-stm32f103ve.md @@ -0,0 +1,35 @@ +# stm32f103ve on-chip flash demo + +## What + +KVDB and TSDB demo on STM32F10X chip + +- MCU: STM32F103VE +- Flash Driver + - STM32 on-chip flash +- IO + - UART + - TXD: PA9 + - RXD: PA10 + +## How + +### Step1: connect the serial terminal + +Connect the board to PC by serial port and open the PC serial terminal. + +### Step2: open demo project + +support 2 kinds of IDEs + +- Keil MDK: open `RVMDK\FlashDB.uvprojx` +- [RT-Studio](https://www.rt-thread.io/studio.html): import this folder by import wizard + +### Step3: build and download + +Download the firmware to your board when build successful. + +### Step4: check the log + +This demo's log will output to PC serial. + diff --git a/docs/demo-stm32f405rg-spi-flash.md b/docs/demo-stm32f405rg-spi-flash.md new file mode 100644 index 0000000..eebc778 --- /dev/null +++ b/docs/demo-stm32f405rg-spi-flash.md @@ -0,0 +1,40 @@ +# stm32f405rg demo + +## What + +KVDB and TSDB demo on STM32F4X chip + +- MCU: STM32F405RG +- Flash Driver + - spi flash (W25Q64) by using [SFUD](https://github.com/armink/SFUD) +- IO + - UART + - TXD: PA9 + - RXD: PA10 + - SPI + - CS: PB12 (software CS) + - SCK: PB13 + - MISO: PB14 + - MOSI: PB15 + +## How + +### Step1: connect the serial terminal + +Connect the board to PC by serial port and open the PC serial terminal. + +### Step2: open demo project + +support 2 kinds of IDEs + +- Keil MDK: open `RVMDK\FlashDB.uvprojx` +- [RT-Studio](https://www.rt-thread.io/studio.html): import this folder by import wizard + +### Step3: build and download + +Download the firmware to your board when build successful. + +### Step4: check the log + +This demo's log will output to PC serial. + diff --git a/docs/demo-stm32f405rg.md b/docs/demo-stm32f405rg.md new file mode 100644 index 0000000..b3dd5a6 --- /dev/null +++ b/docs/demo-stm32f405rg.md @@ -0,0 +1,35 @@ +# stm32f405rg on-chip flash demo + +## What + +KVDB and TSDB demo on STM32F4X chip + +- MCU: STM32F405RG +- Flash Driver + - STM32 on-chip flash +- IO + - UART + - TXD: PA9 + - RXD: PA10 + +## How + +### Step1: connect the serial terminal + +Connect the board to PC by serial port and open the PC serial terminal. + +### Step2: open demo project + +support 2 kinds of IDEs + +- Keil MDK: open `RVMDK\FlashDB.uvprojx` +- [RT-Studio](https://www.rt-thread.io/studio.html): import this folder by import wizard + +### Step3: build and download + +Download the firmware to your board when build successful. + +### Step4: check the log + +This demo's log will output to PC serial. + diff --git a/docs/en/api.md b/docs/en/api.md deleted file mode 100644 index f65681d..0000000 --- a/docs/en/api.md +++ /dev/null @@ -1 +0,0 @@ -# Coming soon... \ No newline at end of file diff --git a/docs/en/readme.md b/docs/en/readme.md deleted file mode 100644 index 916db5a..0000000 --- a/docs/en/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -|File name |Description| -|:----- |:----| -|api.md |API description| \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..72bced4 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<html> +<head> + <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + <meta charset="UTF-8"> + <title>FlashDB + + + + + +

Loading ...
+ + + + + + + \ No newline at end of file diff --git a/docs/porting.md b/docs/porting.md new file mode 100644 index 0000000..f47af62 --- /dev/null +++ b/docs/porting.md @@ -0,0 +1,143 @@ +# Porting Guide + +The underlying Flash management and operation of FlashDB relies on RT-Thread's FAL (Flash Abstraction Layer) Flash abstraction layer open source software package. This open source library also supports running on **bare metal platform** [(click to view introduction)](http:/ /packages.rt-thread.org/detail.html?package=fal). So only need to connect the used Flash to FAL to complete the whole transplantation work. + +## Introduction to Porting + +![flashdb_porting_layer](_media/flashdb_porting_layer.png) + +The main work of transplantation is on the FAL side. Other interfaces are not strongly dependent, and can be connected according to your own situation. + +It is recommended to understand the FAL function introduction before transplantation, see: http://packages.rt-thread.org/detail.html?package=fal + +The bottom layer of FAL encapsulates different Flash storage media in a unified manner and provides a partition table mechanism to expose it to upper users. + +Each database of FlashDB is based on the partitioning mechanism provided by FAL. Each database is located on a FAL partition, which is equivalent to a partition corresponding to a database. + +The following will explain the FAL porting process in detail. For more porting demonstrations, please refer to the demo provided. + +## FAL migration + +### Define flash device + +Before defining the Flash device table, you need to define the Flash device first. It can be on-chip flash, or off-chip SFUD-based spi flash: + +- Refer to [`fal_flash_stm32f2_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_stm32f2_port.c) to define the on-chip flash device. +- Refer to [`fal_flash_sfud_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_sfud_port.c) to define off-chip spi flash device. + +To define specific Flash device objects, users need to implement the operation functions of ʻinit`, `read`, `write`, ʻerase` according to their own Flash conditions: + +- `static int init(void)`: **Optional** initialization operation. +- `static int read(long offset, uint8_t *buf, size_t size)`: read operation. + +| Parameters | Description | +| ------ | ------------------------- | +| offset | Flash offset address for reading data | +| buf | Buffer to store the data to be read | +| size | The size of the data to be read | +| return | Return the actual read data size | + +- `static int write(long offset, const uint8_t *buf, size_t size)`: write operation. + +| Parameters | Description | +| ------ | ------------------------- | +| offset | Flash offset address for writing data | +| buf | Buffer for storing data to be written | +| size | The size of the data to be written | +| return | Return the actual written data size | + +- `static int erase(long offset, size_t size)`: erase operation. + +| Parameters | Description | +| ------ | ------------------------- | +| offset | Flash offset address of erase area | +| size | The size of the erased area | +| return | Return the actual erased area size | + +Users need to implement these operation functions according to their own Flash conditions. A specific Flash device object is defined at the bottom of the file. The following example defines stm32f2 on-chip flash: stm32f2_onchip_flash + +```C +const struct fal_flash_dev stm32f2_onchip_flash = +{ + .name = "stm32_onchip", + .addr = 0x08000000, + .len = 1024*1024, + .blk_size = 128*1024, + .ops = {init, read, write, erase}, + .write_gran = 8 +}; +``` + +- `"stm32_onchip"`: the name of the flash device. + +- `0x08000000`: Start address for flash operation. + +- `1024*1024`: Total size of Flash (1MB). + +- `128*1024`: Flash block/sector size (because the STM32F2 blocks have uneven sizes, the erase granularity is the largest block size: 128K). + +- `{init, read, write, erase}`: Flash operation functions. If there is no init initialization process, the first operation function position can be left blank. + +- `8`: Set the write granularity, the unit is bit, 0 means not effective (default value is 0), this member is a new member whose fal version is greater than 0.4.0. Each flash write granularity is not the same, it can be set by this member, the following are several common Flash write granularities: + - nor flash: 1 bit + - stm32f2/f4: 8 bit + - stm32f1: 32 bit + - stm32l4: 64 bit + +### Define the flash device table + +The Flash device table is defined in the header file of `fal_cfg.h`, you need to **create a new `fal_cfg.h` file** before defining the partition table. Please place this file in the port folder of the corresponding BSP or project directory, and Add the header file path to the project. fal_cfg.h can be completed by referring to [Sample file fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_cfg.h). + +Equipment table example: + +```C +/* ===================== Flash device Configuration ========================= */ +extern const struct fal_flash_dev stm32f2_onchip_flash; +extern struct fal_flash_dev nor_flash0; + +/* flash device table */ +#define FAL_FLASH_DEV_TABLE \ +{ \ + &stm32f2_onchip_flash, \ + &nor_flash0, \ +} +``` + +In the Flash device table, there are two Flash objects, one is the on-chip Flash of STM32F2, and the other is the off-chip Nor Flash. + +### Define flash partition table + +The partition table is also defined in the `fal_cfg.h` header file. Flash partitions are based on Flash devices, and each Flash device can have N partitions. The collection of these partitions is the partition table. Before configuring the partition table, make sure that the **Flash device** and **device table** have been defined. fal_cfg.h can be completed by referring to [Sample file fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_cfg.h). + +Partition table example: + +```C +#define NOR_FLASH_DEV_NAME "norflash0" +/* ====================== Partition Configuration ========================== */ +#ifdef FAL_PART_HAS_TABLE_CFG +/* partition table */ +#define FAL_PART_TABLE \ +{ \ + {FAL_PART_MAGIC_WORD, "bl", "stm32_onchip", 0, 64*1024, 0}, \ + {FAL_PART_MAGIC_WORD, "app", "stm32_onchip", 64*1024, 704*1024, 0}, \ + {FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \ + {FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \ +} +#endif /* FAL_PART_HAS_TABLE_CFG */ +``` +The detailed description of the above partition table is as follows: + +| Partition name | Flash device name | Offset address | Size | Description | +| ----------- | -------------- | --------- | ----- | ------ ------------ | +| "bl" | "stm32_onchip" | 0 | 64KB | Bootloader | +| "app" | "stm32_onchip" | 64*1024 | 704KB | Application | +| "easyflash" | "norflash0" | 0 | 1MB | EasyFlash parameter storage | +| "download" | "norflash0" | 1024*1024 | 1MB | OTA download area | + +The partition parameters that users need to modify include: partition name, associated Flash device name, offset address (relative to the internal Flash device), and size. Pay attention to the following points: + +- Partition name guarantee **cannot be repeated**; +- The associated Flash device **must have been defined in the Flash device table**, and the **name is the same**, otherwise there will be an error that the Flash device cannot be found; +- The starting address and size of the partition **cannot exceed the address range of the Flash device**, otherwise it will cause packet initialization errors; + +> Note: When defining each partition, in addition to filling in the parameter attributes described above, you need to add the attribute `FAL_PART_MAGIC_WORD` at the front and add `0` at the end (currently used for reserved functions) \ No newline at end of file diff --git a/docs/quick-started.md b/docs/quick-started.md new file mode 100644 index 0000000..85b4bc8 --- /dev/null +++ b/docs/quick-started.md @@ -0,0 +1,61 @@ +# Quick start + +This document will help users quickly use FlashDB on the demo platform and experience the actual use of FlashDB + +## basic concepts + +- **Key-Value Database (KVDB)**: It is a non-relational database that stores data as a collection of key-value pairs, where the key is used as a unique identifier. KVDB has simple operation and strong scalability. +- **Time Series Data (TSDB)**: Time Series Database (TSDB), which stores data in **time sequence**. TSDB data has a timestamp, a large amount of data storage, and high insertion and query performance. +- **Time series log (TSL)**: TSL (Time series log) is the abbreviation of each record in TSDB. +- **Blob**: In a computer, blob is often a field type used to store binary files in a database. In FlashDB, both KV and TSL use the blob type for storage, which can be compatible with any variable type. +- **Iterator**: It allows users to visit every element in the container through a specific interface without knowing the underlying implementation. Both TSDB and KVDB support traversal access to the database through iterators. + +## Functional block diagram + +Through the following functional block diagram, you can roughly understand the FlashDB functional module division + +![flashdb_framework](_media/flashdb_framework.png) + +## Prepare the development environment + +Before use, you need to install the following development software on your PC in advance + +### Integrated Development Environment + +The demo project provided by FlashDB supports two projects by default: + +- **RT-Studio**: Free to use, easy to use, download address: https://www.rt-thread.io/studio.html +- **Keil MDK**: MDK v5 version of the integrated development environment needs to be installed + +### Serial port tool + +Prepare `Serial debugging assistant` or `Serial terminal tool`, and you need to use it when viewing the running log later + +## Get the source code + +The latest code is currently hosted on GitHub. The master branch is the development version. It is recommended to download the released version + +- GitHub download: https://github.com/armink/FlashDB/releases + +## Choose a demo platform + +In the `demos` directory of the project, the following hardware demonstration platforms are currently provided. You can choose a hardware platform and experience the running process of FlashDB on a real machine. + +For more detailed introduction, click on the **instructions** in the table below to view. + +| Hardware Platform | Path | Flash Type | Instructions | +| --------------------- | ----------------------------- | :------------ | ---------------------------------------------------- | +| stm32f10x | `demos/stm32f103ve` | stm32 on-chip | [click to view](demo-stm32f103ve.md) | +| stm32f40x | `demos/stm32f405rg` | stm32 on-chip | [click to view](demo-stm32f405rg.md) | +| stm32f40x + spi flash | `demos/stm32f405rg_spi_flash` | spi flash | [click to view](demo-stm32f405rg-spi-flash.md) | + +## View sample description + +If you don't have a suitable demo platform above, you can also check the example instructions you are interested in first. + +| Sample file | Description | Detailed explanation | +| ----------------------------------- | ------------------------- | ---------------------------------------------- | +| `samples/kvdb_basic_sample.c` | KVDB basic example | [click to view](sample-kvdb-basic) | +| `samples/kvdb_type_string_sample.c` | KV example of string type | [click to view](sample-kvdb-type-string) | +| `samples/kvdb_type_blob_sample.c` | Blob type KV example | [click to view](sample-kvdb-type-blob) | +| `samples/tsdb_sample.c` | TSDB basic example | [click to view](sample-tsdb-basic) | \ No newline at end of file diff --git a/docs/readme.md b/docs/readme.md index da9229a..a71ac6d 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -1,4 +1,73 @@ -|File or folder name |Description| -|:----- |:----| -|en |English documents| -|zh |中文文档(简体)| \ No newline at end of file +# FlashDB:An ultra-lightweight embedded database + +[![Build Status](https://travis-ci.com/armink/FlashDB.svg?branch=master)](https://travis-ci.com/armink/FlashDB) [![license](https://img.shields.io/github/license/armink/FlashDB)](https://raw.githubusercontent.com/armink/FlashDB/master/LICENSE) + +## Introduction + +[FlashDB](https://github.com/armink/FlashDB) is an ultra-lightweight embedded database that focuses on providing data storage solutions for embedded products. Different from traditional database based on file system, [FlashDB](https://github.com/armink/FlashDB) combines the features of Flash and has strong performance and reliability. And under the premise of ensuring extremely low resource occupation, the service life of Flash should be extended as much as possible. + +[FlashDB](https://github.com/armink/FlashDB) provides two database modes: + +- **Key-value database**: It is a non-relational database that stores data as a collection of key-value pairs, where the key is used as a unique identifier. KVDB has simple operation and strong scalability. +- **Time Series Database**: Time Series Database (TSDB), which stores data in **time sequence**. TSDB data has a timestamp, a large amount of data storage, and high insertion and query performance. + +## Usage scenario + +Nowadays, there are more and more types of IoT products, and the types and total amount of data generated during operation are also increasing. FlashDB provides a variety of data storage solutions, not only has a small resource footprint, but also has a large storage capacity, which is very suitable for IoT products. The following are the main application scenarios: + +- **Key-value database**: + - Product parameter storage + - User configuration information storage + - Small file management +- **Time Series Database**: + - Store dynamically generated structured data: such as environmental monitoring information collected by temperature and humidity sensors, human health information recorded in real time by smart bracelets, etc. + - Record operation log: store operation log of product history, record of abnormal alarm, etc. + +## Key Features + +- Very small footprint, ram usage is almost **0**; +- Support multiple partitions, **multiple instances**. When the amount of data is large, the partition can be refined to reduce the retrieval time; +- Support **wear balance** to extend Flash life; +- Support **Power-off protection** function, high reliability; +- Supports two KV types, string and blob, which is convenient for users to operate; +- Support KV **incremental upgrade**, after product firmware upgrade, KVDB content also supports automatic upgrade; +- Support to modify the status of each TSDB record to facilitate user management; + +## Performance and footprint + +### TSDB performance test 1 (nor flash W25Q64) + +```shell +msh />tsl bench +Append 1250 TSL in 5 seconds, average: 250.00 tsl/S, 4.00 ms/per +Query total spent 2218 (ms) for 1251 TSL, min 1, max 2, average: 1.77 ms/per +``` + +Insert average: 4 ms, query average: 1.8 ms + +### TSDB performance test 2 (stm32f2 onchip flash) + +```shell +msh />tsl bench +Append 13421 TSL in 5 seconds, average: 2684.20 tsl/S, 0.37 ms/per +Query total spent 1475 (ms) for 13422 TSL, min 0, max 1, average: 0.11 ms/per +``` + +Insert average: 0.37 ms, query average: 0.12 ms + +### Footprint (stm32f4 IAR8.20) + +```shell + Module ro code ro data rw data + ------ ------- ------- ------- + fdb.o 276 232 1 + fdb_kvdb.o 4 584 356 1 + fdb_tsdb.o 1 160 236 + fdb_utils.o 418 1 024 +``` + +The above is the map file information of IAR. It can be seen that the footprint of FlashDB is very small. + +## License + +The project uses the Apache-2.0 open source protocol. For details, please read the contents of the LICENSE file in the project. \ No newline at end of file diff --git a/docs/sample-kvdb-basic.md b/docs/sample-kvdb-basic.md new file mode 100644 index 0000000..e254ee0 --- /dev/null +++ b/docs/sample-kvdb-basic.md @@ -0,0 +1,60 @@ +# KVDB basic example + +This example mainly demonstrates the basic functions of KVDB, including KV acquisition and setting modification functions. + +## Code description + +The sample code is located in `samples/kvdb_basic_sample.c`, the default KV table is defined in `main.c`, and there is `boot_count` KV in it. The KV is used to record the current system startup times. Each time the power is turned off and restarted, the KV will be automatically incremented by one and saved to KVDB. The general content is as follows: + +```C +void kvdb_basic_sample(fdb_kvdb_t kvdb) +{ + struct fdb_blob blob; + int boot_count = 0; + + FDB_INFO("==================== kvdb_basic_sample ====================\n"); + + { /* GET the KV value */ + /* get the "boot_count" KV value */ + fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count))); + /* the blob.saved.len is more than 0 when get the value successful */ + if (blob.saved.len > 0) { + FDB_INFO("get the 'boot_count' value is %d\n", boot_count); + } else { + FDB_INFO("get the 'boot_count' failed\n"); + } + } + + { /* CHANGE the KV value */ + /* increase the boot count */ + boot_count ++; + /* change the "boot_count" KV's value */ + fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count))); + FDB_INFO("set the 'boot_count' value to %d\n", boot_count); + } + + FDB_INFO("===========================================================\n"); +} +``` + +## First run log + +The current `boot_count` is 0, after adding one, it is stored in the database. + +``` +[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ==================== +[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 0 +[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 1 +[FlashDB][sample][kvdb][basic] =========================================================== +``` + +## Secondary run log + +The current `boot_count` is 1, indicating that the last save is effective, add one to it and save it for the next visit. + +``` +[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ==================== +[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 1 +[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 2 +[FlashDB][sample][kvdb][basic] =========================================================== +``` \ No newline at end of file diff --git a/docs/sample-kvdb-traversal.md b/docs/sample-kvdb-traversal.md new file mode 100644 index 0000000..0e7d285 --- /dev/null +++ b/docs/sample-kvdb-traversal.md @@ -0,0 +1,38 @@ +# Traverse all KV + +This example demonstrates that if you traverse all KVs in KVDB, users can add their own processing actions when traversing KVs. + +## Code description + +In the following sample code, first initialize the iterator of KVDB, and then use the iterator API to traverse all KVs of KVDB one by one. + +The traversed KV object contains some attributes of KV, including: key name, value saved addr, value length, etc. The user can read it out through `fdb_blob_read` in cooperation with `fdb_kv_to_blob`, and do some business processing of his own. + +```C +void kvdb_tarversal_sample(fdb_kvdb_t kvdb) +{ + struct fdb_kv_iterator iterator; + fdb_kv_t cur_kv; + struct fdb_blob blob; + size_t data_size; + uint8_t *data_buf; + + fdb_kv_iterator_init(&iterator); + + while (fdb_kv_iterate(kvdb, &iterator)) { + cur_kv = &(iterator.curr_kv); + data_size = (size_t) cur_kv->value_len; + data_buf = (uint8_t *) malloc(data_size); + if (data_buf == NULL) { + FDB_INFO("Error: malloc failed.\n"); + break; + } + fdb_blob_read((fdb_db_t) kvdb, fdb_kv_to_blob(cur_kv, fdb_blob_make(&blob, data_buf, data_size))); + /* + * balabala do what ever you like with blob... + */ + free(data_buf); + } +} +``` + diff --git a/docs/sample-kvdb-type-blob.md b/docs/sample-kvdb-type-blob.md new file mode 100644 index 0000000..aeb44d8 --- /dev/null +++ b/docs/sample-kvdb-type-blob.md @@ -0,0 +1,71 @@ +# blob type KV example + +This example mainly demonstrates the related functions of blob KV. Blob KV is a more commonly used type, and its value is a binary type without type restrictions. Functionally, blob KV is also compatible with string KV. In the use of API, blob KV has a set of independent APIs, which can quickly realize the storage of various types of KV to KVDB, such as basic types, arrays and structures. + +## Code description + +The sample code is located in `samples/kvdb_type_blob.c`, and a KV named `"temp"` is used to store the temperature value, respectively demonstrating the whole process of blob KV from `create->read->modify->delete`. The general content is as follows: + +```C +void kvdb_type_blob_sample(fdb_kvdb_t kvdb) +{ + struct fdb_blob blob; + + FDB_INFO("==================== kvdb_type_blob_sample ====================\n"); + + { /* CREATE new Key-Value */ + int temp_data = 36; + + /* It will create new KV node when "temp" KV not in database. + * fdb_blob_make: It's a blob make function, and it will return the blob when make finish. + */ + fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); + FDB_INFO("create the 'temp' blob KV, value is: %d\n", temp_data); + } + + { /* GET the KV value */ + int temp_data = 0; + + /* get the "temp" KV value */ + fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); + /* the blob.saved.len is more than 0 when get the value successful */ + if (blob.saved.len > 0) { + FDB_INFO("get the 'temp' value is: %d\n", temp_data); + } + } + + { /* CHANGE the KV value */ + int temp_data = 38; + + /* change the "temp" KV's value to 38 */ + fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); + FDB_INFO("set 'temp' value to %d\n", temp_data); + } + + { /* DELETE the KV by name */ + fdb_kv_del(kvdb, "temp"); + FDB_INFO("delete the 'temp' finish\n"); + } + + FDB_INFO("===========================================================\n"); +} +``` + +## Running log + +It can be seen from the log: + +- First create a KV named `"temp"` and give the initial value 36℃ +- Read the current value of `"temp"` KV and find that it is the same as the initial value +- Modify the value of `"temp"` KV to 38℃ +- Finally delete `"temp"` KV + +``` +[FlashDB][sample][kvdb][blob] ==================== kvdb_type_blob_sample ==================== +[FlashDB][sample][kvdb][blob] create the 'temp' blob KV, value is: 36 +[FlashDB][sample][kvdb][blob] get the 'temp' value is: 36 +[FlashDB][sample][kvdb][blob] set 'temp' value to 38 +[FlashDB][sample][kvdb][blob] delete the 'temp' finish +[FlashDB][sample][kvdb][blob] =========================================================== +``` + diff --git a/docs/sample-kvdb-type-string.md b/docs/sample-kvdb-type-string.md new file mode 100644 index 0000000..a24ab6a --- /dev/null +++ b/docs/sample-kvdb-type-string.md @@ -0,0 +1,69 @@ +# String type KV example + +This example mainly demonstrates the related functions of the string KV. As a special KV type, the Key and Value of the string KV are both strings, which are often used in scenarios with high readability requirements such as parameter storage and command storage. + +## Code description + +The sample code is located in `samples/kvdb_type_string.c`, and a KV named `"temp"` is used to store the temperature value, which respectively demonstrates the whole process of the string KV from `create->read->modify->delete` . The general content is as follows: + +```C +void kvdb_type_string_sample(fdb_kvdb_t kvdb) +{ + FDB_INFO("==================== kvdb_type_string_sample ====================\n"); + { /* CREATE new Key-Value */ + char temp_data[10] = "36C"; + + /* It will create new KV node when "temp" KV not in database. */ + fdb_kv_set(kvdb, "temp", temp_data); + FDB_INFO("create the 'temp' string KV, value is: %s\n", temp_data); + } + + { /* GET the KV value */ + char *return_value, temp_data[10] = { 0 }; + + /* Get the "temp" KV value. + * NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible. + */ + return_value = fdb_kv_get(kvdb, "temp"); + /* the return value is NULL when get the value failed */ + if (return_value != NULL) { + strncpy(temp_data, return_value, sizeof(temp_data)); + FDB_INFO("get the 'temp' value is: %s\n", temp_data); + } + } + + { /* CHANGE the KV value */ + char temp_data[10] = "38C"; + + /* change the "temp" KV's value to "38.1" */ + fdb_kv_set(kvdb, "temp", temp_data); + FDB_INFO("set 'temp' value to %s\n", temp_data); + } + + { /* DELETE the KV by name */ + fdb_kv_del(kvdb, "temp"); + FDB_INFO("delete the 'temp' finish\n"); + } + + FDB_INFO("===========================================================\n"); +} +``` + +## Run log + +It can be seen from the log: + +- First create a KV named `"temp"` and give the initial value 36℃ +- Read the current value of `"temp"` KV and find that it is the same as the initial value +- Modify the value of `"temp"` KV to 38℃ +- Finally delete `"temp"` KV + +``` +[FlashDB][sample][kvdb][string] ==================== kvdb_type_string_sample ==================== +[FlashDB][sample][kvdb][string] create the 'temp' string KV, value is: 36C +[FlashDB][sample][kvdb][string] get the 'temp' value is: 36C +[FlashDB][sample][kvdb][string] set 'temp' value to 38C +[FlashDB][sample][kvdb][string] delete the 'temp' finish +[FlashDB][sample][kvdb][string] =========================================================== +``` + diff --git a/docs/sample-tsdb-basic.md b/docs/sample-tsdb-basic.md new file mode 100644 index 0000000..cc25842 --- /dev/null +++ b/docs/sample-tsdb-basic.md @@ -0,0 +1,169 @@ +# TSDB basic example + +This example mainly demonstrates the basic functions of TSDB, including TSL (time sequence record) addition, query and status modification functions. + +## Code description + +The sample code is located in `samples/tsdb_sample.c`, including the processes of appending, querying and status modification. The approximate code is as follows: + +```C +void tsdb_sample(fdb_tsdb_t tsdb) +{ + struct fdb_blob blob; + + FDB_INFO("==================== tsdb_sample ====================\n"); + + { /* APPEND new TSL (time series log) */ + struct env_status status; + + /* append new log to TSDB */ + status.temp = 36; + status.humi = 85; + fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status))); + FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi); + + status.temp = 38; + status.humi = 90; + fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status))); + FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi); + } + + { /* QUERY the TSDB */ + /* query all TSL in TSDB by iterator */ + fdb_tsl_iter(tsdb, query_cb, tsdb); + } + + { /* QUERY the TSDB by time */ + /* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */ + struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }; + struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }; + time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to); + size_t count; + /* query all TSL in TSDB by time */ + fdb_tsl_iter_by_time(tsdb, from_time, to_time, query_by_time_cb, tsdb); + /* query all FDB_TSL_WRITE status TSL's count in TSDB by time */ + count = fdb_tsl_query_count(tsdb, from_time, to_time, FDB_TSL_WRITE); + FDB_INFO("query count is: %u\n", count); + } + + { /* SET the TSL status */ + /* Change the TSL status by iterator or time iterator + * set_status_cb: the change operation will in this callback + * + * NOTE: The actions to modify the state must be in orderC. + * like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2 + * The intermediate states can also be ignored. + * such as: FDB_TSL_WRITE -> FDB_TSL_DELETED + */ + fdb_tsl_iter(tsdb, set_status_cb, tsdb); + } + + FDB_INFO("===========================================================\n"); +} +``` + +Let's look at these processes separately + +- **Append**: Modify the value of the structure object `status` twice and append it to TSDB; + +- **Query**: Through TSDB's iterator API, the `query_cb` callback function will be automatically executed during each iteration to query all records in TSDB. The content of the callback function is as follows: + + ```C + static bool query_cb(fdb_tsl_t tsl, void *arg) + { + struct fdb_blob blob; + struct env_status status; + fdb_tsdb_t db = arg; + + fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status)))); + FDB_INFO("[query_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi); + + return false; + } + ``` + +- **Query by time**: TSDB also provides an API for iterating by time: `fdb_tsl_iter_by_time`, you can pass in the start and end time, and the iterator will iterate the time series records according to the passed time period. In each iteration, the `query_by_time_cb` callback is executed, and the content of the current record is read in the callback and printed out. + + ```C + static bool query_by_time_cb(fdb_tsl_t tsl, void *arg) + { + struct fdb_blob blob; + struct env_status status; + fdb_tsdb_t db = arg; + + fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status)))); + FDB_INFO("[query_by_time_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi); + + return false; + } + ``` + +- **Modify Status**: After each TSL is added to TSDB, its status can be modified. There are 4 types of status: + + - `FDB_TSL_WRITE`: written state, the default state after TSL is appended to TSDB; + + - `FDB_TSL_USER_STATUS1`: The status is between writing and deleting. Users can customize the meaning of the status, such as: data has been synchronized to the cloud; + + - `FDB_TSL_DELETED`: Deleted state, when TSL needs to be deleted, just modify the state of TSL to this state; + + > Tip: In FlashDB, in order to improve the life of the Flash, the delete action does not actually erase the data from the Flash, but marks it as a deleted state. The user can distinguish different data records by state. + + - `FDB_TSL_USER_STATUS2`: the customized status after the status is deleted, reserved for users; + + When modifying the status, it can only be modified in the order of `FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2`, and cannot be modified in reverse order. It is also possible to skip intermediate states, for example: directly change from `FDB_TSL_WRITE` to `FDB_TSL_DELETED` state, and skip `FDB_TSL_USER_STATUS1` state. + + In the example, all current TSLs are modified to the status of `FDB_TSL_USER_STATUS1` through the iterator. The callback code in the iterator is as follows: + + ```C + static bool set_status_cb(fdb_tsl_t tsl, void *arg) + { + fdb_tsdb_t db = arg; + + FDB_INFO("set the TSL (time %ld) status from %d to %d\n", tsl->time, tsl->status, FDB_TSL_USER_STATUS1); + fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1); + + return false; + } + ``` + +## First run log + +It can be seen from the log that the example first adds two TSLs, and each TSL stores different temperature and humidity records. Then obtain the TSL in TSDB through ordinary query and query by time, and finally modify its status from `2: FDB_TSL_WRITE` to `3: FDB_TSL_USER_STATUS1`. + +``` +[FlashDB][sample][tsdb] ==================== tsdb_sample ==================== +[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85) +[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90) +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] query count is: 2 +[FlashDB][sample][tsdb] set the TSL (time 1) status from 2 to 3 +[FlashDB][sample][tsdb] set the TSL (time 2) status from 2 to 3 +[FlashDB][sample][tsdb] =========================================================== +``` + +## Secondary run log + +In the second run, two TSLs will still be added. Look at the results of the query, there are 4 in total, including 2 TSLs that were added at the first run. It can be seen from the printed time stamp that the analog time stamp works normally. `query count is: 2` shows that although TSDB has 4 records, only 2 records are in the write state. + +``` +[FlashDB][sample][tsdb] ==================== tsdb_sample ==================== +[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85) +[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90) +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 3, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 4, temp: 38, humi: 90 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 3, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 4, temp: 38, humi: 90 +[FlashDB][sample][tsdb] query count is: 2 +[FlashDB][sample][tsdb] set the TSL (time 1) status from 3 to 3 +[FlashDB][sample][tsdb] set the TSL (time 2) status from 3 to 3 +[FlashDB][sample][tsdb] set the TSL (time 3) status from 2 to 3 +[FlashDB][sample][tsdb] set the TSL (time 4) status from 2 to 3 +[FlashDB][sample][tsdb] =========================================================== +``` \ No newline at end of file diff --git a/docs/vue.css b/docs/vue.css new file mode 100644 index 0000000..d9b71bb --- /dev/null +++ b/docs/vue.css @@ -0,0 +1,829 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600"); +* { + -webkit-font-smoothing: antialiased; + -webkit-overflow-scrolling: touch; + -webkit-tap-highlight-color: rgba(0,0,0,0); + -webkit-text-size-adjust: none; + -webkit-touch-callout: none; + box-sizing: border-box; +} +body:not(.ready) { + overflow: hidden; +} +body:not(.ready) [data-cloak], +body:not(.ready) .app-nav, +body:not(.ready) > nav { + display: none; +} +div#app { + font-size: 30px; + font-weight: lighter; + margin: 40vh auto; + text-align: center; +} +div#app:empty::before { + content: 'Loading...'; +} +.emoji { + height: 1.2rem; + vertical-align: middle; +} +.progress { + background-color: var(--theme-color, #42b983); + height: 2px; + left: 0px; + position: fixed; + right: 0px; + top: 0px; + transition: width 0.2s, opacity 0.4s; + width: 0%; + z-index: 999999; +} +.search a:hover { + color: var(--theme-color, #42b983); +} +.search .search-keyword { + color: var(--theme-color, #42b983); + font-style: normal; + font-weight: bold; +} +html, +body { + height: 100%; +} +body { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + color: #34495e; + font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif; + font-size: 15px; + letter-spacing: 0; + margin: 0; + overflow-x: hidden; +} +img { + max-width: 100%; +} +a[disabled] { + cursor: not-allowed; + opacity: 0.6; +} +kbd { + border: solid 1px #ccc; + border-radius: 3px; + display: inline-block; + font-size: 12px !important; + line-height: 12px; + margin-bottom: 3px; + padding: 3px 5px; + vertical-align: middle; +} +li input[type='checkbox'] { + margin: 0 0.2em 0.25em 0; + vertical-align: middle; +} +.app-nav { + margin: 25px 60px 0 0; + position: absolute; + right: 0; + text-align: right; + z-index: 10; +/* navbar dropdown */ +} +.app-nav.no-badge { + margin-right: 25px; +} +.app-nav p { + margin: 0; +} +.app-nav > a { + margin: 0 1rem; + padding: 5px 0; +} +.app-nav ul, +.app-nav li { + display: inline-block; + list-style: none; + margin: 0; +} +.app-nav a { + color: inherit; + font-size: 16px; + text-decoration: none; + transition: color 0.3s; +} +.app-nav a:hover { + color: var(--theme-color, #42b983); +} +.app-nav a.active { + border-bottom: 2px solid var(--theme-color, #42b983); + color: var(--theme-color, #42b983); +} +.app-nav li { + display: inline-block; + margin: 0 1rem; + padding: 5px 0; + position: relative; + cursor: pointer; +} +.app-nav li ul { + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: #ccc; + border-radius: 4px; + box-sizing: border-box; + display: none; + max-height: calc(100vh - 61px); + overflow-y: auto; + padding: 10px 0; + position: absolute; + right: -15px; + text-align: left; + top: 100%; + white-space: nowrap; +} +.app-nav li ul li { + display: block; + font-size: 14px; + line-height: 1rem; + margin: 0; + margin: 8px 14px; + white-space: nowrap; +} +.app-nav li ul a { + display: block; + font-size: inherit; + margin: 0; + padding: 0; +} +.app-nav li ul a.active { + border-bottom: 0; +} +.app-nav li:hover ul { + display: block; +} +.github-corner { + border-bottom: 0; + position: fixed; + right: 0; + text-decoration: none; + top: 0; + z-index: 1; +} +.github-corner:hover .octo-arm { + -webkit-animation: octocat-wave 560ms ease-in-out; + animation: octocat-wave 560ms ease-in-out; +} +.github-corner svg { + color: #fff; + fill: var(--theme-color, #42b983); + height: 80px; + width: 80px; +} +main { + display: block; + position: relative; + width: 100vw; + height: 100%; + z-index: 0; +} +main.hidden { + display: none; +} +.anchor { + display: inline-block; + text-decoration: none; + transition: all 0.3s; +} +.anchor span { + color: #34495e; +} +.anchor:hover { + text-decoration: underline; +} +.sidebar { + border-right: 1px solid rgba(0,0,0,0.07); + overflow-y: auto; + padding: 40px 0 0; + position: absolute; + top: 0; + bottom: 0; + left: 0; + transition: transform 250ms ease-out; + width: 300px; + z-index: 20; +} +.sidebar > h1 { + margin: 0 auto 1rem; + font-size: 1.5rem; + font-weight: 300; + text-align: center; +} +.sidebar > h1 a { + color: inherit; + text-decoration: none; +} +.sidebar > h1 .app-nav { + display: block; + position: static; +} +.sidebar .sidebar-nav { + line-height: 2em; + padding-bottom: 40px; +} +.sidebar li.collapse .app-sub-sidebar { + display: none; +} +.sidebar ul { + margin: 0 0 0 15px; + padding: 0; +} +.sidebar li > p { + font-weight: 700; + margin: 0; +} +.sidebar ul, +.sidebar ul li { + list-style: none; +} +.sidebar ul li a { + border-bottom: none; + display: block; +} +.sidebar ul li ul { + padding-left: 20px; +} +.sidebar::-webkit-scrollbar { + width: 4px; +} +.sidebar::-webkit-scrollbar-thumb { + background: transparent; + border-radius: 4px; +} +.sidebar:hover::-webkit-scrollbar-thumb { + background: rgba(136,136,136,0.4); +} +.sidebar:hover::-webkit-scrollbar-track { + background: rgba(136,136,136,0.1); +} +.sidebar-toggle { + background-color: transparent; + background-color: rgba(255,255,255,0.8); + border: 0; + outline: none; + padding: 10px; + position: absolute; + bottom: 0; + left: 0; + text-align: center; + transition: opacity 0.3s; + width: 284px; + z-index: 30; + cursor: pointer; +} +.sidebar-toggle:hover .sidebar-toggle-button { + opacity: 0.4; +} +.sidebar-toggle span { + background-color: var(--theme-color, #42b983); + display: block; + margin-bottom: 4px; + width: 16px; + height: 2px; +} +body.sticky .sidebar, +body.sticky .sidebar-toggle { + position: fixed; +} +.content { + padding-top: 60px; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 300px; + transition: left 250ms ease; +} +.markdown-section { + margin: 0 auto; + max-width: 80%; + padding: 30px 15px 40px 15px; + position: relative; +} +.markdown-section > * { + box-sizing: border-box; + font-size: inherit; +} +.markdown-section > :first-child { + margin-top: 0 !important; +} +.markdown-section hr { + border: none; + border-bottom: 1px solid #eee; + margin: 2em 0; +} +.markdown-section iframe { + border: 1px solid #eee; +/* fix horizontal overflow on iOS Safari */ + width: 1px; + min-width: 100%; +} +.markdown-section table { + border-collapse: collapse; + border-spacing: 0; + display: block; + margin-bottom: 1rem; + overflow: auto; + width: 100%; +} +.markdown-section th { + border: 1px solid #ddd; + font-weight: bold; + padding: 6px 13px; +} +.markdown-section td { + border: 1px solid #ddd; + padding: 6px 13px; +} +.markdown-section tr { + border-top: 1px solid #ccc; +} +.markdown-section tr:nth-child(2n) { + background-color: #f8f8f8; +} +.markdown-section p.tip { + background-color: #f8f8f8; + border-bottom-right-radius: 2px; + border-left: 4px solid #f66; + border-top-right-radius: 2px; + margin: 2em 0; + padding: 12px 24px 12px 30px; + position: relative; +} +.markdown-section p.tip:before { + background-color: #f66; + border-radius: 100%; + color: #fff; + content: '!'; + font-family: 'Dosis', 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif; + font-size: 14px; + font-weight: bold; + left: -12px; + line-height: 20px; + position: absolute; + height: 20px; + width: 20px; + text-align: center; + top: 14px; +} +.markdown-section p.tip code { + background-color: #efefef; +} +.markdown-section p.tip em { + color: #34495e; +} +.markdown-section p.warn { + background: rgba(66,185,131,0.1); + border-radius: 2px; + padding: 1rem; +} +.markdown-section ul.task-list > li { + list-style-type: none; +} +body.close .sidebar { + transform: translateX(-300px); +} +body.close .sidebar-toggle { + width: auto; +} +body.close .content { + left: 0; +} +@media print { + .github-corner, + .sidebar-toggle, + .sidebar, + .app-nav { + display: none; + } +} +@media screen and (max-width: 768px) { + .github-corner, + .sidebar-toggle, + .sidebar { + position: fixed; + } + .app-nav { + margin-top: 16px; + } + .app-nav li ul { + top: 30px; + } + main { + height: auto; + overflow-x: hidden; + } + .sidebar { + left: -300px; + transition: transform 250ms ease-out; + } + .content { + left: 0; + max-width: 100vw; + position: static; + padding-top: 20px; + transition: transform 250ms ease; + } + .app-nav, + .github-corner { + transition: transform 250ms ease-out; + } + .sidebar-toggle { + background-color: transparent; + width: auto; + padding: 30px 30px 10px 10px; + } + body.close .sidebar { + transform: translateX(300px); + } + body.close .sidebar-toggle { + background-color: rgba(255,255,255,0.8); + transition: 1s background-color; + width: 284px; + padding: 10px; + } + body.close .content { + transform: translateX(300px); + } + body.close .app-nav, + body.close .github-corner { + display: none; + } + .github-corner:hover .octo-arm { + -webkit-animation: none; + animation: none; + } + .github-corner .octo-arm { + -webkit-animation: octocat-wave 560ms ease-in-out; + animation: octocat-wave 560ms ease-in-out; + } +} +@-webkit-keyframes octocat-wave { + 0%, 100% { + transform: rotate(0); + } + 20%, 60% { + transform: rotate(-25deg); + } + 40%, 80% { + transform: rotate(10deg); + } +} +@keyframes octocat-wave { + 0%, 100% { + transform: rotate(0); + } + 20%, 60% { + transform: rotate(-25deg); + } + 40%, 80% { + transform: rotate(10deg); + } +} +section.cover { + align-items: center; + background-position: center center; + background-repeat: no-repeat; + background-size: cover; + height: 100vh; + width: 100vw; + display: none; +} +section.cover.show { + display: flex; +} +section.cover.has-mask .mask { + background-color: #fff; + opacity: 0.8; + position: absolute; + top: 0; + height: 100%; + width: 100%; +} +section.cover .cover-main { + flex: 1; + margin: -20px 16px 0; + text-align: center; + position: relative; +} +section.cover a { + color: inherit; + text-decoration: none; +} +section.cover a:hover { + text-decoration: none; +} +section.cover p { + line-height: 1.5rem; + margin: 1em 0; +} +section.cover h1 { + color: inherit; + font-size: 2.5rem; + font-weight: 300; + margin: 0.625rem 0 2.5rem; + position: relative; + text-align: center; +} +section.cover h1 a { + display: block; +} +section.cover h1 small { + bottom: -0.4375rem; + font-size: 1rem; + position: absolute; +} +section.cover blockquote { + font-size: 1.5rem; + text-align: center; +} +section.cover ul { + line-height: 1.8; + list-style-type: none; + margin: 1em auto; + max-width: 500px; + padding: 0; +} +section.cover .cover-main > p:last-child a { + border-color: var(--theme-color, #42b983); + border-radius: 2rem; + border-style: solid; + border-width: 1px; + box-sizing: border-box; + color: var(--theme-color, #42b983); + display: inline-block; + font-size: 1.05rem; + letter-spacing: 0.1rem; + margin: 0.5rem 1rem; + padding: 0.75em 2rem; + text-decoration: none; + transition: all 0.15s ease; +} +section.cover .cover-main > p:last-child a:last-child { + background-color: var(--theme-color, #42b983); + color: #fff; +} +section.cover .cover-main > p:last-child a:last-child:hover { + color: inherit; + opacity: 0.8; +} +section.cover .cover-main > p:last-child a:hover { + color: inherit; +} +section.cover blockquote > p > a { + border-bottom: 2px solid var(--theme-color, #42b983); + transition: color 0.3s; +} +section.cover blockquote > p > a:hover { + color: var(--theme-color, #42b983); +} +body { + background-color: #fff; +} +/* sidebar */ +.sidebar { + background-color: #fff; + color: #364149; +} +.sidebar li { + margin: 6px 0 6px 0; +} +.sidebar ul li a { + color: #505d6b; + font-size: 14px; + font-weight: normal; + overflow: hidden; + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; +} +.sidebar ul li a:hover { + text-decoration: underline; +} +.sidebar ul li ul { + padding: 0; +} +.sidebar ul li.active > a { + border-right: 2px solid; + color: var(--theme-color, #42b983); + font-weight: 600; +} +.app-sub-sidebar li::before { + content: '-'; + padding-right: 4px; + float: left; +} +/* markdown content found on pages */ +.markdown-section h1, +.markdown-section h2, +.markdown-section h3, +.markdown-section h4, +.markdown-section strong { + color: #2c3e50; + font-weight: 600; +} +.markdown-section a { + color: var(--theme-color, #42b983); + font-weight: 600; +} +.markdown-section h1 { + font-size: 2rem; + margin: 0 0 1rem; +} +.markdown-section h2 { + font-size: 1.75rem; + margin: 45px 0 0.8rem; +} +.markdown-section h3 { + font-size: 1.5rem; + margin: 40px 0 0.6rem; +} +.markdown-section h4 { + font-size: 1.25rem; +} +.markdown-section h5 { + font-size: 1rem; +} +.markdown-section h6 { + color: #777; + font-size: 1rem; +} +.markdown-section figure, +.markdown-section p { + margin: 1.2em 0; +} +.markdown-section p, +.markdown-section ul, +.markdown-section ol { + line-height: 1.6rem; + word-spacing: 0.05rem; +} +.markdown-section ul, +.markdown-section ol { + padding-left: 1.5rem; +} +.markdown-section blockquote { + border-left: 4px solid var(--theme-color, #42b983); + color: #858585; + margin: 2em 0; + padding-left: 20px; +} +.markdown-section blockquote p { + font-weight: 600; + margin-left: 0; +} +.markdown-section iframe { + margin: 1em 0; +} +.markdown-section em { + color: #7f8c8d; +} +.markdown-section code { + background-color: #f8f8f8; + border-radius: 2px; + color: #e96900; + font-family: 'Roboto Mono', Monaco, courier, monospace; + font-size: 0.8rem; + margin: 0 2px; + padding: 3px 5px; + white-space: pre-wrap; +} +.markdown-section pre { + -moz-osx-font-smoothing: initial; + -webkit-font-smoothing: initial; + background-color: #f8f8f8; + font-family: 'Roboto Mono', Monaco, courier, monospace; + line-height: 1.5rem; + margin: 1.2em 0; + overflow: auto; + padding: 0 1.4rem; + position: relative; + word-wrap: normal; +} +/* code highlight */ +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: #8e908c; +} +.token.namespace { + opacity: 0.7; +} +.token.boolean, +.token.number { + color: #c76b29; +} +.token.punctuation { + color: #525252; +} +.token.property { + color: #c08b30; +} +.token.tag { + color: #2973b7; +} +.token.string { + color: var(--theme-color, #42b983); +} +.token.selector { + color: #6679cc; +} +.token.attr-name { + color: #2973b7; +} +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #22a2c9; +} +.token.attr-value, +.token.control, +.token.directive, +.token.unit { + color: var(--theme-color, #42b983); +} +.token.keyword, +.token.function { + color: #e96900; +} +.token.statement, +.token.regex, +.token.atrule { + color: #22a2c9; +} +.token.placeholder, +.token.variable { + color: #3d8fd1; +} +.token.deleted { + text-decoration: line-through; +} +.token.inserted { + border-bottom: 1px dotted #202746; + text-decoration: none; +} +.token.italic { + font-style: italic; +} +.token.important, +.token.bold { + font-weight: bold; +} +.token.important { + color: #c94922; +} +.token.entity { + cursor: help; +} +.markdown-section pre > code { + -moz-osx-font-smoothing: initial; + -webkit-font-smoothing: initial; + background-color: #f8f8f8; + border-radius: 2px; + color: #525252; + display: block; + font-family: 'Roboto Mono', Monaco, courier, monospace; + font-size: 0.8rem; + line-height: inherit; + margin: 0 2px; + max-width: inherit; + overflow: inherit; + padding: 2.2em 5px; + white-space: inherit; +} +.markdown-section code::after, +.markdown-section code::before { + letter-spacing: 0.05rem; +} +code .token { + -moz-osx-font-smoothing: initial; + -webkit-font-smoothing: initial; + min-height: 1.5rem; + position: relative; + left: auto; +} +pre::after { + color: #ccc; + content: attr(data-lang); + font-size: 0.6rem; + font-weight: 600; + height: 15px; + line-height: 15px; + padding: 5px 10px 0; + position: absolute; + right: 0; + text-align: right; + top: 0; +} diff --git a/docs/zh-cn/README.md b/docs/zh-cn/README.md new file mode 100644 index 0000000..fdbfd34 --- /dev/null +++ b/docs/zh-cn/README.md @@ -0,0 +1,79 @@ +# FlashDB:超轻量级嵌入式数据库 + +[![Build Status](https://travis-ci.com/armink/FlashDB.svg?branch=master)](https://travis-ci.com/armink/FlashDB) [![license](https://img.shields.io/github/license/armink/FlashDB)](https://raw.githubusercontent.com/armink/FlashDB/master/LICENSE) + +## 简介 + +[FlashDB](https://github.com/armink/FlashDB) 是一款超轻量级的嵌入式数据库,专注于提供嵌入式产品的数据存储方案。与传统的基于文件系统的数据库不同,[FlashDB](https://github.com/armink/FlashDB) 结合了 Flash 的特性,具有较强的性能及可靠性。并在保证极低的资源占用前提下,尽可能延长 Flash 使用寿命。 + +[FlashDB](https://github.com/armink/FlashDB) 提供两种数据库模式: + +- **键值数据库** :是一种非关系数据库,它将数据存储为键值(Key-Value)对集合,其中键作为唯一标识符。KVDB 操作简洁,可扩展性强。 +- **时序数据库** :时间序列数据库 (Time Series Database , 简称 TSDB),它将数据按照 **时间顺序存储** 。TSDB 数据具有时间戳,数据存储量大,插入及查询性能高。 + +## 使用场景 + +如今,物联网产品种类越来越多,运行时产生的数据种类及总量及也在不断变大。FlashDB 提供了多样化的数据存储方案,不仅资源占用小,并且存储容量大,非常适合用于物联网产品。下面是主要应用场景: + +- **键值数据库** : + - 产品参数存储 + - 用户配置信息存储 + - 小文件管理 +- **时序数据库** : + - 存储动态产生的结构化数据:如 温湿度传感器采集的环境监测信息,智能手环实时记录的人体健康信息等 + - 记录运行日志:存储产品历史的运行日志,异常告警的记录等 + +## 主要特性 + +- 资源占用极低,内存占用几乎为 **0** ; +- 支持 多分区,**多实例** 。数据量大时,可细化分区,降低检索时间; +- 支持 **磨损平衡** ,延长 Flash 寿命; +- 支持 **掉电保护** 功能,可靠性高; +- 支持 字符串及 blob 两种 KV 类型,方便用户操作; +- 支持 KV **增量升级** ,产品固件升级后, KVDB 内容也支持自动升级; +- 支持 修改每条 TSDB 记录的状态,方便用户进行管理; + +## 性能及资源占用 + +### TSDB 性能测试1 (nor flash W25Q64) + +```shell +msh />tsl bench +Append 1250 TSL in 5 seconds, average: 250.00 tsl/S, 4.00 ms/per +Query total spent 2218 (ms) for 1251 TSL, min 1, max 2, average: 1.77 ms/per +``` + +插入平均:4 ms,查询平均:1.8 ms + +### TSDB 性能测试2 (stm32f2 onchip flash) + +```shell +msh />tsl bench +Append 13421 TSL in 5 seconds, average: 2684.20 tsl/S, 0.37 ms/per +Query total spent 1475 (ms) for 13422 TSL, min 0, max 1, average: 0.11 ms/per +``` + +插入平均:0.37 ms,查询平均:0.12 ms + +### 资源占用 (stm32f4 IAR8.20) + +```shell + Module ro code ro data rw data + ------ ------- ------- ------- + fdb.o 276 232 1 + fdb_kvdb.o 4 584 356 1 + fdb_tsdb.o 1 160 236 + fdb_utils.o 418 1 024 +``` + +上面是 IAR 的 map 文件信息,可见 FlashDB 的资源占用非常低 + +## 支持 + + ![support](_media/wechat_support.png) + +如果 FlashDB 解决了你的问题,不妨扫描上面二维码请我 **喝杯咖啡**~ + +## 许可 + +采用 Apache-2.0 开源协议,细节请阅读项目中的 LICENSE 文件内容。 \ No newline at end of file diff --git a/docs/zh-cn/_coverpage.md b/docs/zh-cn/_coverpage.md new file mode 100644 index 0000000..20aeb29 --- /dev/null +++ b/docs/zh-cn/_coverpage.md @@ -0,0 +1,15 @@ +![FlashDB](../_media/flashdb.png) + +> 一款超轻量级的嵌入式数据库 + +[![Build Status](https://travis-ci.com/armink/FlashDB.svg?branch=master)](https://travis-ci.com/armink/FlashDB) +[![version](https://img.shields.io/github/release/armink/FlashDB.svg?style=flat-square)](https://github/license/armink/FlashDB/releases/latest) +[![license](https://img.shields.io/github/license/armink/FlashDB)](https://raw.githubusercontent.com/armink/FlashDB/master/LICENSE) + +- 极低的资源占用 +- 飞快的检索速度 +- 键值数据库 +- 时序数据库 + +[GitHub](https://github.com/armink/FlashDB/) +[开始使用](zh-cn/quick-started) \ No newline at end of file diff --git a/docs/zh-cn/_media/flashdb_framework.png b/docs/zh-cn/_media/flashdb_framework.png new file mode 100644 index 0000000000000000000000000000000000000000..befbab05e1fd1e448067a95f273366b83ca52f85 GIT binary patch literal 14300 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 G>i+{(!6VK9 literal 0 HcmV?d00001 diff --git a/docs/zh-cn/_media/flashdb_porting_layer.png b/docs/zh-cn/_media/flashdb_porting_layer.png new file mode 100644 index 0000000000000000000000000000000000000000..cc5574e668951553c206755cbad0ba4d18b6a24c GIT binary patch literal 19238 zcmd3OS6GwX60UTWD!n73fPz5iy(20hAOfM6(5r}{NfD#;A|f3OMX8~8q!@*S5_$<8 z1u2Fmy~FuH|9|g&cP`Gw`JM+1!L`<`nRm^ad1pen&Leg5tISs~UAjd6;J)hPOPBD$ z!0Wxs#K3oC<((ejA3V>;>dKc&dRTt}KM>k0X(?U0R31xuY()h8eC6qVBhO2hD88M4 z;i290Z7yAU`1ygV(i0!^)uw|aId$I!sk8oL%2TV{7r9MU(MVz&7geq+cQe^sEmjYm zh`7oxUs+Ygw<5c5X_82e|GJDmk^VZ>he3HcG& zNR3%a{e9S;;l#ieCu=PtC`L#WNY18%xa<6d9llWoQuzG66vafBvM7-A#%AmpBy|Se zayD@`ecaY^BY?b5e_FWprTLaR>6cDw;w6pD4$NK%FvrUDEP5SpR zj1bh@TP*w#FIuKMQQc=-GpCMBJ6c=XXIp1{#D(S?Rzwl6b&b90G$w3qUj@8dGBo%A z8nW!S7$}rygkTseY+$w+if<3;7OujSal+Sy4b86Tjo3%sFLx!QT;NrfIzes^+fq%? z{EW{=KAx7oE#lDsJVvW13J$W9X@5ta2+CC~B&W}Pun>uM>Hq!GAj3dT_-0$pj^xvf z_e_#01r9d(d8bxnJ1+=n7@|#g0~5*U1I!p)oq=WDE(F&Mrq!ZacujYUn)oUg^)lok zxIX*QZhM)am7BHM_zkN^)`1Md!ooh=X}ue2euo+nzRJ1f^0{G}1JbelL;;**mQ0l_ zf7E$h4ff{Xi=vgz_PaWRLYtDzu8-}69T>~j_vqaXP!{etbPqS)QxPPI(fb{Lf(u6cQ7AnDKm*Y78M&0f(LUJJD0UbmL29M zfKueff9`<=Ye<2WlCv&EuH4%z#_4mj_v(gla#~3XaWnEqby00)xjxwNn9^lJlozap z{<{ilrC2jg=+RHd`71D(Ro`bFzw-3P(R(4Mr?d}_-u`-f#VBD!49vtj*kFeS5GwMX z|6Dh7?H}I?DItVAY=#AsyG)^<4t3KEQ1=;cxoUqF=5!e(atuR$C3Dm-F8QW$_>nV2 z$QouF#q{tT&|@A#)WS5$Oexv>&9vMETgqQ$hwfe0Bi?yo#8@*bKJT9^m-1GgKm{iZ z-qsFyJnK0nQJij7g{!zfMF~I>qw@z{u=492PdYGV%r_yyad8i_%qQ8aTwS>v#_5GQ ziyk+zag&QM(45?|IAV>@&LaHpfxbvts~h+GF~1oFOXSP(X`_>HmR=4y=1tZnofS1o z+^T%{nLhf-xN1n{hP`KEVgeN>w+Z9oD-@LPoS#RPj&p#@L}t2Q+lZ z-tJQ64ZT~H33B2?xHyCA`)0(XA|bk+*P}sTKmG^S!*}IR2Va;36d79nXROsMj>0hk ziJ!s%pzVRtaE)erp(x4{Zy~0+LN5HYQ*=01TI5Mm&97zYR@cg!XwZb(g*zd73{SD5 zyN?Qn@8s0V14|Y4F1-@$ik9 z5Iu0MtauHwrVFpaUE@6Zxrga1c;sr6uc0s&%5H0&Jj!se0pf9Ob$vsY$Z?GsVi0%k zM>KT95bPoxgGc^bZg%I#Mao(ApTbu zW5qd6)alRk&XqQHIr%MTi0z%^#IvK)(;DA{=pcAv>9@D1F;cm3;4T8!qz$OoaC8y$ zo~BiG$E*I?FS@rdyi-r`c;ay=+{l+4gu2Pi$F!;1{IR&68iv0OovwN}LQMRdvI`Eh-OSXl;kgCeHW}A`*Mn8%I4HMRN zoho-0L_kGN_r^Eq7=yZLi^?2<-Vm1ESXz`K|Hv@t1U?}ArIma&X^j7`Em&SF62218 zp?ECsoYrzmIi=L>2veJ>n8WK{d$K1KrOgpq59eGZn_`%IsUu>rf=s3>g6o_Kw zT7<2-lp&&uVb6OHylT{98VDVUSnb^v6_b#+kxV)e|TbE>QG8UG89c5DGq6Q z8K9fjQ+i~`u_f)3;3EC644LMO#CdciCo5Mgpyc$c*X@Mn?KHku&NMT9(3$!sBp;wT z6^p9KU1q$!Ji&0IXiFlbq~@rzp$sVge*^`7YJiV%(5qZib~b6xf5j#tJWT=kqWF6II=d_sCzgUR$1pq1f39mFl1#* zdQ!OjyPg}z+MsBP2G4r$YNc%6P>IdTovvm23z!jN-AsylUG>{e$xs#Khme)8;1v<>olLFY*82KkZfxx(vdmD>~2SUy*bS6 zHCwm2KeX&wIu|ZMsJq@G5P4)4%88ws?AW#?Gn!b~|Mht!cv(EM=(6ol!A`zg+Dr${ zKp0rPTsf*O*-#3kI*D=(+b98taH8KL#Z^Q{w&UB*Xo2Hjtwh_W;`WqK9BO;{CVq+% z7;DK!PaVaHEN!R`vo55%6_a#|X0r8n$8XOQIH*Ev*Vvm3_XpaH@T%;K2D)%etmLef zk{MWJ2wdwXGr%T?W!ak2REfQYk}ahFBij!@cI70Ll~b52&Ia`GSmYp1;1b+6WIw%) z*<<39yNk9|%itqJmRI{ZG2|n5|DFZHt7IT>7?IMahU6;Z>Ucu$Twx$tLy#+HUQ#lmfl+`m+3}cadD`1cIkwuP5G-|iah8^f6_ALNCsP6-7B7!bEHsm1XUC}0F@yAw``SXCXk@Oysv|V&*&%veajIll^+=xs{d5xB zY4yhecW=a-M`0^Ff@`t8QGgWL|1={i(j9Z_>Jkk=pk>Wd` zl%0-$-09V!5*D7NTqgH%Wm{<3wf&zQds=$0KsWdGC9bl~y+pojSeMQ%DUU!!ttP-% z)d9T4HLOu>jGR__BFo!lt+SUXeO-FRe=a8<_I!GsF(!3H)0vl%4Cl=8e*7=G8&)GV zO2+g(@E~OLuBe)hJo3#;0lJgC?!4tKfIkRv#X*FZ|8v})o8{seOj*+HZxH)FCQl^8 zQ-GL(ZOdMxB6kncSJhoQE#va$tX{+<&EeM!7t%rVwb|so*<>dA49rRb9QX-W$)(hJ z!v8^)bc|C)T_+=T&H&yo$}+7ep3YGoXyvGqcUZq)_A4emRxFuZ2=k60pUAiWXk`J= z3RPx+gkiXfKZl~kNLFxHCyB<)&RjJ#&XVj(;P5JS1DK)Tnw3}YY(Iq!jv-#%lA4Ge zeUHpuPZvaG^s;gU#1-7I2*?qD--eJ?Y^iYI0+;%2c0Yt2w#wrw;vfDwd+M>G&Uc{x zWZ}BNNx$!+NN-< z5cZZ=+xpXWl)h5hrnV@F7Di&j*I`Hj!4Vzyai%Jk}lk%7tZ8T>*QB{#&6x)s@{d_-#FQ)Kw z$=y{$NYKDvYcBmbG{C6Vl`P9z7xvKX>^w3p76zM9O+fwo;01b@_cJY)^ATA z=Z%EumqnA0JpXU;@$%DLT*I6$JK-LXQPEXr=#uc@mC#YYl20s#5kee(FYjkdeFsBj zuM^x7p+!eDY=EVR3GYT^AHKg*IL>;bMh9yq))R-3^+-SN=~lD7E~0h>m4~oQ41)5> zQL6W%R=GS()tX}eQ81d)qsW=9GTXn6_~~kC>h-4qSU=UYBI^%c1%}If4Rc0yOv5pjM*+*GCC@U8Z)s$gC-Y+B5-Zs zYe>HP4W78a*k&)rS>F@lZdEcU%@sF>`r4HVJs7eeI_VQ%Q9l@u)nqKAjLBM)@DR+= zFHO}=I)?H{vg)m*{d4&yf)-l;I*|Sx{Q`ZOIDv+mTMB7}x`s5VQGNr|Jc=I5Ul+lp zq~jAI;TZ9_KYrgfN@~OAP&fqP3c>7NyeH}m+llKv^ZmX^Qn%>o&IGA>E!_-Ru^-p@ z-vDvIh1|du5kaNU)H|hphGDF{wbHiB6ykhJ>}*lAyYq0#CkmvFM%GQlqWR6$&O^!p zesTlaA~}(rA?>u!@<;ItJv>3Cb#vhyz#4?ssr${#mF|h)q+b!QQXJp3U)=Nos>OHw z-hA)kfA|@gt3b$HoYp~1^}F&xYP{9G#Wu9SEYjDeh#Nisr&$NnOitOS9XFA>9T z$rEZUL?~fYM;R+KsJ7Z{s+nKXXI|M727kEBETSIa#?NEFbJZtm?);AZl){8KVfwKe z+tN=lwA@WzEYq~mT$)GV7&Z1N%-0%@zVt%bm#pgq`dN%LV^g&+x&9gIXB6FU)qgde z?NGkrJ;h`0Qc|}_IS?$0a^Yz`uI30TcX;`_!kPPON^?|}TkSpR`S488)#ZJp=0Qz{&3EKt8CKmdEbgFgLO|8rOH@8SnV3cJevm!^vpX*z3RiR*f*tLL`S zsWqw0(>vdZhN#r%1EgVR&ew{F1c!@Ia{xhRVB}@N%y0K5PyV2Ip)>Ya*R#L7_v4@a z9Pq6le9Oj54PO)7>HT8qd&2?4KbDrh5uwBaH;b>l(8m~{58FL#1ZmUkTB#VrAjx51+lQ&HHw?*LjqdKT*5^XxP^i8Lgev z!4$jN9|Mc8%n$=BSX{+wMPREtE%6QDHUBbKXUjhn0)~UIq&*oO#j7(RwU03 zc^Kj==U;$Ze>HoJTCO%pHrSVylmoO;()9&Fsgq9uu$?7JJL@vpYoc~N>E2Ti+h?d& z^%>4fFZl_Y@a_}uY87(8+t zvOqw?Rs{=>B~N_Q#mdQ0i|w5dIB+qxTZla8lE_HNMhIrAkrhu}DN7qqa~8q{o1R}^ zPbqDlls)=dM6wwZSv&2w9Cey}068ZyVB{mzq*yPH!{EipEUBX*28y4R5vr%qy}(t) zW}nkGtj0k@>#mS)m7YoyNArfAvE7`Q1#oNj!r*5MOdcad&}7xy;wcVOdp>(D6xRAF z;SA*kuKTQpY2mt?K|ca0IYtfqlusI5_?;>>XqLJpEKYw++ed3*K_jlkB8gQU&H0oo zpWQ##N6+WRo$D|EsVf&D4_-{WUGPKsd809%%5OEx5tnAIxS*S5$Z=T}ExZjiIVL=} z@yW7|Zw|vdDKE2#UZ!Nq$BW?J<@u%D{vxVFLQqjczYQ9(ggP2ejr5hjFSkBkqS+Cn z_&L0@D4CdsB6jX5xr!B6{!N4M7@_5SdApaLj{2CIi|`ZQXn_jLa+uQH=LmbFgBunBG4T`0z3SgdzSbXeaBaN6iei-7)oDRlFa+{EkP_(g7k{NMh$LG7!;wbXuU+)V+Y5 zb*>S8nPUv@ku3uG6>J@a1LaQ`izSW+&1?)q{(DLyxiiBr6nJr8Z8r2O&0n?9i>L78 ztPvHqPnFol+cU|pSl*M{65aZ8KED`I08Mg3jDaK0kEu0J^d-E^DZ!r~LQWr|b`m-F zq_*0L-!MVN%>KVrBSYwk{cMnO{Vn`TTH=>$)P!XM>oC``N3KKnoF=AaM#axTrpw45 zXo015VEMks$tS)n+-A9vBFr6pr*uO4Dy(hYleJ3=0;{$$eB(U9`BWt9M*H$!>3jHa zlGLlL%DYiCt7>{EdmXm!St|bd6kjXAoCZ_dSP(cu?0iTcZQ>xUs2~X@pvI3 zcrNbSH2L`eiGv36sijavdVUF+8b)S?ZmEEnbo;AcfnTTOreSRfn^IFHDM^*}lI5^Ed;r6AJh>|qXjeGnWlwlS) z^yu0R5vo-FHbbg*6@n1PXnBXE06SY&6}#kFc_xD49R8vii9{C`4v~AK+EXn#j9spP ztzMf5I3KpbClTNtG+XPykx2vu{SIYq!A4oH0o@-bWBh^=si z$BW&7QAj=csO8-;{U77^E}Nt@+|pW29$tX8y;}9S_4Iw7 zyp0`%7st@Ye$vO-mo!?kxeOR_E|XsbT!eZ&7m}47wJ{_)M57@YQ7-%IuQ1}5pW#>a zR^7jplYE&8Ass|UEv#r`Gil9s$cvOhoTPqh19066E-Y|wZ+&lI^yCr7JB7_{CG}Vb z4)>2KAGf`#7y#kuj7MpLNLLitNtnMj;~v0w;X7IBGG#c5aDy)98!ifw*DcEvKa*^S`^^pTEE&xb(8^RlBge(&g^E4h@)on z*+Jqi;O#5d-zBA=4uk_G04W)I2xh}$t(tTdn1Iuif5XfVZ3wKl!YB-OPy~TVRG+aE zFP}$%ztW!L8kgBMV%r!!{i#AVeM*|vS;WCdUw0Ns$P8!&SRKXBFL~7)QWmhCg|C(Y z3&S^lCQTifZUyUUe?(hHNy&$O>ss@vso$Lw1Sq;ku4TJqz-^aoX0LtIgj|ty0aEtr zk=*G$&n}fxI!+)|C>1{s9sKxF$#ZkBSm17R8}$nf+Ev6w!xYD@Q`#p(#LHwb+aJPq zJe&*H9*AC|spNTAd=G=P>`#*Uu&?X6{H;{te?yoHDbB-;)bl6?2r~md5657;RNIM* zf8;3eL-U_c`tt{4UuD*Nn4U-I+}HFvp9m zMiH-RXsm?2MPSg@8tJ;@bYM?$cWFj|$`;>xeeS`0WrKb)tAx8B|`cK#EE0SUleQ?m5e%WR92HG-xs z(4A($){h7;-^_(EawlHvs1!K7AHH$pU2RZ(t331539gxS+JA)2MmB79a3rZGHQs-L zqnWt~D^n?_4qvp{G_7}%r0$veTS(=4e+aVQN?V+oK1TWGrFo}2N2TCcF}tQx8(6N1>6}xVp7P%&xdqtF>4y^u@ek`vgDZw!)KQ=&10{sl^jRxhFBpD}3^1 z7_Lee2*xLjYc{;@Mst|RY;+yhY1p?4ziQKin02?cMr6d-Hi$BDggf=lKP$4u+3XaC z*`x&bAT3v0LO<#APt!oEqXV~$2`qYk#IkMXZ+%UZ$C{3^v!LyxQYbc__jkhuH`LOv zxwo42M~hR0t7~C)4NEDSKc`$I4kEAwQtS@guj8vNhF)osovLF5yc1#5dt~^B&~m0X zu1+SyC4|Vu52q@#GYH@QDfgB#X!~HZ{S}>5R1Ro?%3KzMi&Z#0K~HT?ygDr!XN1B^k2~4JJ*G^&r^FO|Mzw3*6fGtO zRZmMIjjWvk55_}nfNMP=|RdH2KK-^e*-s9~s`b$ke>c!MYO` z9(gUvUxC7B+lL37Ut7Lc<4X`gFwf~h!^}nEJrVUza}=Ixtkkg^Q+zClOUiz|i-88O zt1n||E*m;m82`9?W4K|2#e}5DcAscQ9F;DV%zF*pD&nE#pQH}6-FXNQ_oiVs-0%O` zT151kEQH9}vBV~K7N;-u$aD6G+~@lS_Ig7B6QV{Olmu?}PHJqNIG^tG{W~2>#(T*MP9Tt3~fos{Y#e zZYm6EoLm$D`?ROlHHY*PFZM2@FIqti+@1b5F19^wbhAjpu;O(6#+ZywQT{|b%3FB; zr0C>Bu_f_GR=&DY(j zy<7Yf&iSu#UvC}%V3>^oXy?~sY~8%tcj zuz)&37lmPQB=IP~lF^TQ^x4>+*Fw7cEZIz(W^ajqnBVjks6YZ4E62RMRkcSim&9|+ zVYt)Zyl#Y7#J)#<=gop^lE3)f3A%0X?T>6`Mc_ZVwkX)cu3aNr_S-yVq%90cm*uxh zpZS!&nh$K!1~I^FqsPeufn)=21r}&N>FPFdZyGlBUdK}Wgt+lW`p!0^Tc&0A58QKY z6-|2mcjhZv5dOTqE&G03tx4w6Z`ISPw9c$gbw*>nh*r_PPl04e!wmIi&VTofArD6v zI80@lAl~bF@k{%Q9SvIEopJvDMl5}4nBypZv)7{XVpowtyT?3{`UnG(sv!jySoJ67 zw&=$aT~K7$Q_o`ZD-t$tjgX0{o5`Vu=4buEjRLSY;aP4quRgj~fWCws|BjZ>)5~6U z!Ud2Xny~QqNxkhToaowL=u()RV29nGk!<;iUcR{&zq-VNrmj_Zw0o+1{8%PTj|D+b z(cCyuMk|;&eUN)8sQM&34gP>+kg+m=YQ0r%hFX~<;{IlG8TCINzs4-5&M2k{ez=d=6*|AiRO!%I0ip-W-$F0PPDOCbiVM2Lq&fFYSluUi zpi60*bKE^rXMT}q6r8K0FWP{xlGFTXUA)mC@o;}ea||neHH(eC_0y2>;8;5f0CUVQNQI(Qj*5U_DAn$gh2^hfKn9 zj1uEn(U?}-Az}^Rjl=Kz9pCOdTKJFDMFrfHqC3U@n)jBg&13V|&1FPvvL}*{C^wEP zSv}o1kvgUsy-5BwFK|w?=a*!?ZaXZ+gkW-{mGE>69E&!Pun%;igZbk;=RRplY^G+} zg|j#}6}zdD&w|EXua&l|D-==hE?SnE*@wj=>y->#=nFf%fopuE0Es#gN0O3F#v_vYwXH zj)e-D4^!9}@kfzBx_jb(+g#zcV63p@vEiY{`u;=Dwd&a13xJ1tPk;K(qSUC`ECg0C zVn&FZ**^M1TcqUN`$c?*;EPsl?+&n$dv3|XyhD)-Z+eHqC&+J!JT^OMIKZFZ4wuFK z@}S+R#@F9-Y%jlLTQy^QA9a0@W&*Ltx1@m9&+$|_bI{)D!1@xd@}<77TOSui)x1Gj zxaz2fJoqgY1M)q@$dAHN@5*y6#wCshchj|N=9xb}!q&0gS~S}O_fv4oJCJxd!G<44 zGQcW%%&|a3S;DizmR5oErZpa-vzbYl*cG}zhA+*ltE#kB@L z&RdgM4E7(lLM)5F+%S}H*=vgTdh)(w?3duGV<-@1Yc_7LrhAt7-emh@dxkidZqhSP zmvW4PHmuq)dUl~6m)qGL!(!KXKp>&zmUxH}--(9ujxS0R7p-a6ocSGPp`T3-K}s4| zeBMh8S+=tDcV8jP>Te@EUm8BJ=5qo0lcV$4 zIJMS?2@=D>g7{&>Cy`9<_O_wm83f^8s59e#AU6%hD!H;%Bboj9(ONY@n>Gd6n5 z!-_q+ zm%9{POO6B^6R%&c;ERL5^~)GGbl^PwWxp-TKWE!|(b!)CbNi|8TDIR5tJU`LkZDib z+)K7I&8h7AnuLfvw58!(qw4U2u3bfT+vElZ)@rts$5V&Wk^(^Xp!x%!1=0QWnE;a7 zqC%F;L4g?Y7jB8mCROs=ZUL^+ZH6}}q%@zMr!|Y*K!a8QsJu%XTpg+{I2LwFM*f%=4u02r{T?Ygkf+sR7FFL6H z(MygB-YdzC7vxB^HjdRxhw3%x3-*mV8=HQZff%w_|#Q*d20)iE8C6ss?CVazSi7`T@;$0zTfx+oul7TELme&ZY`nfx^`Sb;1w z{40?-SRD<9sp(J)VN`huUgc64)WnVA&4x>% z{e6y{ZjsQN0oBeCQ1uEQPGE@JlMVL9OE*b;?9su%7X*1{pHbi)?Nx*)f9RWk^%W%s zrY6LH^Q;*Ef#^%co7d?^hq%5w!H9-Fr3Y;9@VF5WdZkwn+(VSgmzgCYv&eC3mF*r+ zT~u0mvdatGFsu}SP~ZIw{D*8CJkTsN4NKA%Z4Da`3d$bA#Va4{Kc6!Je=KvK7b^xP zbh-N>HA9tvj@&RB^H{D6wWNqC(uFS(v{bG`RbRV~3WmgbjdH*p32a7r=NRj(JRsIG zHTL{VLFCY3a{~&f1?H(sSxDA?7!9-an+F6~F>Gyj?AWdU;8Z#Nn&F52N&Byg#2OQh z^xeG;0+!;urEi+~6td+DC|dTAT51zcpRS6uf;vQPb?%-0qM3k3gVC2# zSpi&7g{CTAE|~-IRie_L|7Y@hM4n&PuQA~d7jzRHxbS?-rsH7ZS2e}gX1aIl>X8*Q ze_%zdpBR}Fq}4+geo{uRK;$}7r2P}h#Fr`P0;_*KA#FZvmEJx$n(Xlp8*%e01Gq6J zMqrD!11Chvt&Jkn%c3h9SN$z;X@~clO?zEOWBbkUlH>cXWr|0oTeI?9Dc zga?e=DcmWFfhgBDFs!Y4G{&DSQPW_`f{kQ81u3!&P>e5xj5rf3gsW^AxCH_rmLY%_ z{wpYmSOf0NrRIyVsCK$ZFkq{RI}SU{O!BhL9t|13vB}%Z&UAHGMK17a4bx`WTgXst zSw(`#cM)`_e6ByFYXz9wgi57Vn;xC2s1<&<#zJjKDiHhgYG3I1_Xi?3T;jEWFj%bG zLsg@rR18aoCU$-RxI`Z|wrbx_9ky5ONZ6q#?NwF9uZ)K7?Rn`9{)tq&5^JS^aceGZ z=$q}*5j{;nz8j-)(Ti0}WauyF+79EL`)Yrv+_IelPx&SKtsFUiH_U=tHie$*bL$g3 z2d8{PcS?seON$}3O6z88gOHe-c6Au-ME`!i!uSW_;%9@4KKuhQb#l@-$RiJ`lqL>c zMgV{NSE|dGf+y{-E8>+JF)Cb>-`s!jTjS-;t5tXL9?KD`;Sbt=Y}n-5CK*+Cby*_y zT_KGpIX3ToK1S)wI{CKV{T*jQ8DYo)Y8w2{QA zi~=tU>6KfP-~rd20*FbGHXJIiduJudSrAjf;0_E4NOKa^aSe1If%3}~?+a@{*d-vd z5g*>#-WLImzY9lK0~x)|5tn<(>jPA>Q3OaL^9AAJ&lW}Ov6Tc%Y)bH=0 z#y3KMxg1}LSs6kIgN-@!ER5=xM8MMAOojS<;OdmwWDzb$1HkkbvqXW;N82ofCEST3 z0U9j%ab<=2X&g1Z>>xW}crtsj;)aL`^p!kJf1OJNJd_m>lNUJr5)zJf4f)?Wwo`bc z9=M7+ZRxh;RY&L(lPX27JlsI^<9BF)UicpL|A%;yDc1svW z#CRu)Zl3WybUMQnYl)W|b5f=~zr%zW*Rdtma3B&$m0&?^{PFwrf)FyGzC$6q8Hu_z zU|VM6;{*|Y4I(CWQW>@{TM{|ZGlC;K55KKf0fv_cxXkRQ!EIfP1mfV&Dl<-6Nl5Fq zhs4gqwI@!8K$m&`876WQ6x_f4oQXdGCR8(HU-ra6ArKQYfdHyI7ieUH0EvnB+aEvd z%@UdTHLos2BnVngKs62E$Ou&QGhBs2O2If`oO(U(m$vK>_gxY2`>v?rBK@DXCn81v zv)8*qV##l%6`1+SF6R<#DK(RYYd);meBW;mxFD;x2tOy8RIazkh_|6vijFv83B!43 z8@-~Io4j3Qc}Cs!SlI^mvhn>Yr186O2H5joY!^T`?-|Oc4vZ^kD=7-tw76iQ8$riB{*i+;^94=%@r}t3K;m=v$M~k{hFk9I^4w!1AoS*M!E%ZU&QVk zNRwur+0%38b?J)|tXlBhIngt;Is(p#N|8{LSv&YfFs{@%^0!aG83tYcAI7x`H83-l zG&9q9^z}umceOzLgpB=u*q`kx zCB4nFgWU&M1tWmvX!$WH$iP+ykfeDxZVLx6sZ6;=i#gp~Z^-^FI1TmvadzC@Wh~%& zdLSd#&^B$N$FI~XwDnD+=6;-K)xBMC1k8!K!)8S1qsS=W(1U`96sL)XcYb09#P0 zJvH|{XP?(UZw4>OG0Yee?_?~_j2$od)-nVGG#6RmT;t#^w{Q#0<&CKN8_nUKvrYUP z3i}WH@upjp8Tvwv82O!t4yz=TeU7AR#odF>?sk`+6{V%tHm#$c#UBsvna>>K*3<7C zcg@th-6}MxG1;uCbT{p*c`(s(TIwcnbo+?L-(0y~azDkDA%HJ`3ffo0=K2)#0&T|j zLRP$5|8R8I0+JlF9`n-ci%f5ev$jl_gt=Ri*U|}}`BR=p46!7XPJieqesX%4?zD%` zadbs%D9wDDwBh7Q^}_5#^Qh_5xxslBne`T2mT=i+k+oq)Phrc1(7IcJGeN`l5t5s0 zgd1AvTjZI-1OfXy*#x#bcdq@zi6C@o>R005%rtM$b-P<^ZanimE~X4X928TYy8S>< z20p)4_?cv)wsCRxg~kh=arc7^4TZSuP4K#+`GmyIx5_Q z2(K#ss=2H;UR4_-pV*GpEygVAz?~{OU1a6Z%v#xPW6zU3^E(9S&lLWUP=%YQ$`Mlc zLsL&HnL4sE(1ML3hWazZ)bRl|KtIkLb2djRPM>8^@1+K#Zk_V}=OAzoq&ff{2)&7D zDcQ%iZSF`&+xvU%GGVw6S6uN{1;#Bh1MXQLI61hpRft=*#2or9c%=Ix%DV^7Tui;g zvf1uBknkC($&CA$6tj`EuwJxjiSogSNbx4%oSVl_@Vy|*4}G)VEB(v}ydG*DRtZcV6NQUu;^8ka4h@6EJoh!(NWv@k_D0_hCRMpPS#10>3U~q zL1`2VuGpnWYC6TDc_~=2*%o8TYqSF~BRUUc_VsR)RL%8s@J~14j1GD(T2(TT_{8|= z+j!sJZ0dJSupb1<_>?Bu7m?lUdWm6X@SkAx%=Rh3h@Xq#of76go9-i6+57Oih$j)y z1H<##M%#OPG^`&?4 zk<@JjD=dF3`JCz!oZvv?ZsaI@2;?fLfr zwKAKshz?_BeqxG^Xzs3b7s=Sj;fOZhHr1p{SNNlLzM4AAxS?)+xh#sm7Pl^FAZ8>b z#1nB|@vt+aNq6o!F*Pi^^uxT&|A$$_Yoj5;@BNADyQSLkK$&f|y6h&O1b0rWp+>yd31#Hw<$RmK`JgmApEc@71d`+$PGe&lK>u zK;OiTFS{Hxq394w?$|7h>}`$X(u6JE$~I_u9d{yD1q80YAMwWk<$XLUMQu3g00C4B zV<1NPg(l^?MbZHm2L#i=n+p^{T+XijX!css?r|xjplU0cwCw7Jmiy_F4L@tk#=MLS zYukH{G3~FPZMvOW3@X)J z7iE-O;;z|h=hn>cjn2c_rX;KD$u4Fjj8zEoO1Y;oQdma!o|8g-#D!YejFtvZi%2hq z4^YfjIFM-HOQhM&|MQudN?|a4>C`P^HGk7~=okLi_RF$%Ki5C}R%fjg$LxM-$@pQq zbw9qyn?li)EBxH%h2wo|!q}L8SjCV60ZiZA3hea0R0Fa= zbY>Y~AwwT~rLx25qFHi{EK-8jV`X|_qz8wm@O&Kz13LK6gw$!L@*Wn+>A2dsL3!bP@O05t=c zr*amk$;)K2wHA;YM7BA5pKnQO9bnFV%9_eIWr8cb@0!jzTTT36wRm=@F+cH1Lhkp} zC*)xV(?4;KRT4FQG^dOu$Q0wX8 zHOb4!>RaS#-hON>PG4ei=sUVv^7WrK3Eeu&_}O9Tw?T65&ksswdEK(Fx%o{#sjRr& zw}>%iy*1%b_DJEkzx@=nlpN=kY$02f2TN=ZTq~{=bvwL z#WsnDq{nkYN8T()d%2d;k6N;^MTDD)G8wPdAX9-)uw1Z8)xA_(HUR7A;1c>cmZ5U= zqSEBKwL)6DHau93)K1uoTg~Avf<2AT3mo8K+6IUE5C0ggU%MR%6yXb}DQA4P?z;bG+saT5L07bd73CON)7ZR`&|KR;0vX_w$n$~K%BGOXSt?7)D z6bGn;^ZanZvTVdEp!(eeK25Jj4mY-_ntICTv`DmbQyGlpLAA{1h<@L0WxGVy0#ikK2 z=txK<6uE9HNtEcHutx$ZY*un#b&?VjG)&tpasZic<)qRk47s$3uK{zDeLl49h?x8yBy6L6}G3e_MM&9gf!> z33dAD5uuCp`sqAxc=E)h=!5kqaq!RK_`8QBhq6EaOH@P&OE=>XcW{!fLSZ#&B-q6sONnl?*3NSCiA#sOCTvB?J#hGo$aOvLI)a79SL z4R?%C_a}b&Eg7rn`N}aZbt>??lzGMKbE{xkcW}bRI*t=7 z*QRQi20bl=Nc^>Q&9G&(qaGfZ#y!&v$r1+|@ERKC7b>~0iaSAcDv&+G$$(XQitWCb zQg#-Vk_SF}gi9&O6oABrsb^;yWYn=OG9r24&wAt(=Cl+8@D7QfywZ_<0dXGN3N;5y>+Nt4Ke&D0Uh%H05&rE+nFGK>Xj)3ATx_S{k=eVl) zswTIDW@6FXZQ#xzx!AOi6!E;CV9gciiT*(-a*~W59dR8C5wuASNjG`jEMB#2&Z2^9 zE`c2k;FuQEPX@hl!>BOf!;j0#SYN}FlH+e*0ds+Eg%3NImv#A!U1$`%wJfg>>g7+Y z;neAA%v?tfiejez(sH98eLRq!o;U6b(`GIczI05YYM(a)jEcQcl=77b#5;?P(Aiu7&Wl*@y|v}H%1>y-)=?W9~GfPZp7w)x!bvpg`_uv!vm2kIZT%V@!kb_2XHP{Vnp z6~Q3!zB%p9EBlN~I(^$jPzJMLrh?_Zpct-__m`lDZav1)zeH^Qh>}QH&5mw=>sds1 z6IEMMyH*XdSy`6$FDZ2_nMsDTK~~bpN0fk4K@2B1%;()ftYv1qp=_}qYjXdmdgz4N zNfk1cL6ah(&*@4Fhd0lM@JTnnt>~R#@9R{Vk@R&&LlG3meZP5s6)5@P) zM$(KsCh_+=Au@M*b!1G>#vl1^^k*O5ZlWFH35r4v$c6dK?`f@RD*Pi+q-B)Idom(z zwkH9I7c|k&iR@INH(3e2jxpM=!(^w@f6<@|=Q0Dv!_gdngsf*o2y7sJN!R)>Wbk#? zl{CD5HsuUyA6-7Kw2#ZT)z~uQ4c@m~`}!htCt~&!m4Ge;QS3mc-TJF1N<3iJ<4JwR}Qc+i#@9 zF2U!z*&GNwzUSd5O6w{rN|;w`nA3kZ13+U602zDhOPoBcH5tw19@3*Rb1-G=QicI1 zvsymEF}~luR;#<-O4Tj4^@lMYeeGlQw637!YN0&LHdllJ)&>=_@%X z`sBWu^PqrT>Yhtk4FPcb0@tP9Yyy!9xe}Tj+TM*t^rlZEh0}mPJrJc9Z)LFX3A-dw zgz_W&kiyG?SOti-3(9R(Kpe~sv?=C(d8qGihA*U(j1VBOg;lftr2tE(+K9VU|A&(Q z479C>{}1`^#lXT(12oX1qJZAz|C#opUH$x`+ZZ=r}fhmcn2sl~(JUERsmH`eExY3J&h^Uf+2_XYN_5c~Roi{F0 z3;o-;_-_y=jtx9+dnU(EX2AEZSRV7uvZ=n0_aev@smehFPFNZ>dNC5IfxxT2=qDYP z9(N}<`!G-SOM|=2GHDqO+Rdv=-T-L6{3XgEFLirr~VO#)Xl%a0S$#akQ)@S zO)9Jf;_}X8!zne1Cp{928Mae0CNLsX(pvdrKTllzj8n;RJBJuRCup9>&KIAZsBb>i z6RgI5TW+(Yi%SnjW(#h2oQLq)4rsZcQ73e5BoMZiA)U&7Rt2tJ1tvA?YLFEWz<&K7 zimeFxf7{e(S5mbY>e(iBG(00U+&Gs$k?LK&_|CJ_XQc(1~r}5buXnYC3XfjP4 zy!~S>FK7V>>b3e%eS#S~Ki+sX{@VyCdxevt^l(aHdN4*<%6YE(L;9aSqaHx5HE(Sd zmqI#yUmP8|cfhjcX41WzJh@<>1m%9WFGajK%{=HUZZRwg-7`B--1-o??%dO`#MKQj zPL&7UTW;>>JXp6(kCR<^7mT710SiR^g*B}vh7(+7xfve^gX)Glw|2E z;JI^q0i~feirHF?hA6mkx-y+VG3SX`_jw}L1h7SenSHhLort*tIDKaldt&SrcNhCx zjd{?vDGO(T=GDk~Uzr(2|1*8cgxwy1bZmPKnZ)@2VD%`GvRT6&-4A#R8MRmnY?|lFJ2yMMzn!pZ z2cN<17;d9Gd*&b268~CRazkkK2O zAF$>5{MQQ_E@s)k4pjVVaCgu8quXqp_Pttse)_fu&ScgjXXMX`K7Vug?as$CMS7OY z=S26Io&L?e`R@KI$e_*cw1vUloeO}yhd%jCcHlZpLrvv(KNcSW4N+YQo}dTyA@I13 z?(5$!iHcO4Uop`T`w*!exrgC=bySS7O;E~H?&f3W=YBlnxUg=fzLkG>@wcFzY!<*6 z{KOYm-IuyI)Tsm<_*%M*NPNzyxzw}z*ImzeiQau7>o;)yZluWi>*DH0)*M)jzMlw|zI8o{)r)gg_u61OiBr0HGry(xiT)k$>Ay>* z(nlI>+`BpVgL35_9MYdp3w){&4Zwiodn6Lk-v#lT3!8&znP7OH$*TV!(fsz${{`n$~T z@?`bQGtao!n$MBHnW|MTm(%x|a#ns{uwa3U==a`xuc9rpozBiq_quDZz1Ho^|0CK5 z%mFb6+&fsHAg>~2-Us;N7r&?nea9VltT->vG24{y(x!>P?|%2Yx*gUJDy}u#0i1Q# zS^7F=*%iGRy@(VJ(9zLB z3l}b|pl}l&e)wVc`=N&(>b9jk+Wge1Q}yR5r<|hu0`BDZ+>Z@D4tY@xMAk6{x=PIt ze((c*&a34DKKt3v>T7=f^Pl^+XDU(c?d=u6iOiJWmn~c7+s26}o>-xkdzCOh-Jnn? z^wR-8ZuplW+D}RH{^obB49t;79_b=MuJLOZ<`KZ02!2|y*PA7pIor=#$x5YC7556@ zH{8!+q~iIaxB+^4dfYw$$@ttHL=QApTMGVVh~AX@(QC$duYLdh_gy4ifBp6PoY%c5 z;2k7WBbGM&+@*qSvz==8^9wJ$(60}Gr=EIB|Gw8=dsY0tIrj6vLo?rEi1rS)Y5EBG zu{(g*))Y$FV~;)bAhOJJ@Apm+FY>+A(bCeQ+j!%RH+=gmhiO)s@X9N%sQd1_ucH4Z zQdqOs?X&Xe+peFv=zWHFT>cdlzQqvjb!=4Yq#&`atxfH;(@yGlzx$p3Tbvc&IA^vq5Mrv?^m6|B=bv}`ch_Bax$jr4UuGbMl}z6; z^j^QEV2h;^iM9&uSVD)l-+nv2_10Syi^Yf)C)#J9eOz;iSt-5t+H3TcuY84=a%sEm zwyQ|lK|8zcwj1rf`|iXDr>3SRKSi=aOZIBaXd@nvYg0|kuWII+ABgsES4kzRsj1PQ zBSws%V~#mSYuaY}a*x-1!! zn=jwh)z#1WZ^8{X+@ODBR_C63_St9E#GI-lEK}3VxcgP`!==&ag@In2zH9jtpL|kZgMf@Ye@noi5bZT#OhlN7o;7Qh?xZ*n)~#DtVR`^= zzWHW-?usj}(0}{2Wm1R?1}2YC2-13_Uw+6mCyO{j%-^KH(P&h+cj1K>x_!ZKix)4} z=V9KN=goVe$*0rlir)n;zW8Fducl6N_uY5*qvpAD=k}ZU_`9{WwPNf)Dh$fkt;#-K zZ^Xu{iHd($v~EVY{#>(WjeCv!W*He8(w?+0Q7Kc~{rcCx?q2`RZ+_GL?(GBGx66KP zw8yNFw%>kx+F^$sT#Z)lVK%EjndMis$NGZ)&Pr27+h(K+nmn{_WkkT_xp*c3QLGs= zX1M2hVpWU*;DZl7s2D3hP+LL+AIxm@0?{$BNJOgVJ$~*Sh zWA!zUKKf|I{a)EAznRqXlb`&=r4)G1q;GQ1Hrs5Y6&$%2=b@m=z!LociV=C_mf60%5Hdrqy6P(Z-TR_~9Jn6kUI4UO`EB9Cg%$UrJsfhq z+yjM4eC2*#JJVbOTz>iG{rvCd611GOackt`ObIT-{m*V`0K8_PJfqsq#cpD zKmPHL``rhSYrS44Xv$tUtVzl8?wFuxAEu(5m)}esLhkoNv?y{ozWUX#>NKYH>({G$ z?zzWxr|z@QJ{9+E2^qavh!$rA+8j;DQU>d!U1u@8})V&GB@J(`HM-heEWMy5YMO2LX;LQ>OSn z%-bdg44xo1f84(T(k`f0+BBm>fCCOVphB^e^WXmVx3wqA>jxK*?9pPeQg+MV^Uj#3 zzWBvsDiHGD|NY-O6V!YUfN01#kY^6?A`%WBHm6))`=$uzzyl9d)2C1GckM@o0TJ!z zjx;}I&;-sr^UMm0@U{mqUrJnwqQICbymCEKb>$jUhMGzZ#DsT%^CdQUknEq&@{wkW zO)-V!RX)e(xtV-1sS5hYY9tqmX;rkkH}=^s=<{Z+muUt8AXac5!5IN_JWQ4@87_X~ z3c!RkISLds6TJ5fbofElFr_#F#ED@U$$8!+K2>%WAl;R!|={h)9=Lw*n2j>Pte9}KSZ3=dlY>bz}bAuE_ z|4L;Cjk5JQ3Iz3-1_DJs3VeN#FOQQI%JF+4oEg;Dp>MzX)vxq(#*7)G`-S)Z!WX_k ztf1*8UwP#fcZ}p6CQX{8Q}2@96!(XJ$8V{= zyh`?gstPKZOz~$@w`sC|dFB&OJmL1|M?dCazijBYvhBn{>Hap~<4Du|nMDZibOC5Xc zaTUr}q2MS)KrRrSW&|@Os8o_aQ;e`ID_6x%)mK=qHe_B^@AdvgEnfeidTZTX>aDgr z)Z19ZH&2dkm11OMnd(xh z4n_(iH^Y=$%BfzpSS{XgpL*tBzzj>&-ec{3Cj<@FWvA?=|(E;kN zHxE&FzHyYg_pOuEqpx3}-dKK@>c}ooS*uI+q_QfN?^BsnUZuMEJ5zWGY@keJi*y(< zU!l}Nic4JvG7j8|^?AZ@cZbT81LQ-IOQtJjlM^ z{qA@4)vtb4|BawQTW2@L<9V{J1jRyO{a*iEd?m~6Fijgfj|#;Cg@Q4%f?-OlrS!(~ zJE=Xplp^sEg(5}1KFtkV#1g#xjIth3ER`!m(4x)1M#$oG*<6l7)_9sYW`CMAVn0e` znIr7CT5&Ly2HPjU$|~oPp>4~v76`Llo!4a>5vS{~zn;GKwXf;Fe{jJ$^wS^zk`V0b zE4@w6Ed3p&!!J@eS4RhIdjr)4x28;R9lg5j_q3+-acXJ}Qz?)jOJTqQj6ghxCsT0a zYvLOev=Ad*X{Ak3kxHb(1@0$LK2=AVWSFe>7TROSgJ|p4U08=}qWQ1PqaOQt8Wx;D zN6)&NBDv`lWIb~J{P}d!Nhj&HfyB!$yUbPE0GQnJ{)m_X!6%-0A{}=Y|=l`b1-np9IZu>pON?p`!e}<0V?`n#v36xCr(%iXo_1Y>J z2a~c`9MQaa^JvJBA-ep>I}U#10=WC`yXo0ypQT^?;uku((j1%3!PTGpk|4cX@Xh!X?JY!hp9$-Ktxe2FMu?4~?SpLicFUilEMTepyQnQFS}{^hnxg_E%_KdmB$0izbdbl%j>PR1+*xG#0JMUX=-T_~D1UDpP`1h zIG><{XI(_2;zyB{;W=pa@bzKMQgMh#Vx^I0&YbBcVj%ed{s6qQuvo7MlL=yu8LqEi4sU|ru_ad8P!h{L#?;_7~ z!5oE)Ofe;vy+NkKy0!HD>(|nnQ3>+3%Wx5N#sTd$U(A zm=CLlSY;&8%WTKrXdg2glnm|lzWeTDB(r*CF&hr`Q6SsMgU*O-%+m|+T}dm}KTP2` zQ$c{WahAb}U?bJUS&kK1>4QA>o1_LsYXXMnOBwygC3e8Yy&eZlhLEC6F-`drYvUmn zAEgvgrk3VT|0%VE_M;eOxepFIhEfU2hOz{U1SPjpPAi@}p)$yA$Zr9pTa6q!a=@|k zj=6mLt$SuFxB8u{A=FOF5#D7e!1e_O(J^PvSpf$Oq({1jz9i* z-H1u?LiN=O$txaautju46gUBN9+WPuN+Ua5qy(Eb$adG^z|3Q_ zh-#=V7UzKufdF|#A`WX-j=x$82Zk#uM{b>REOf=5Ud+Iim-Jr8y3HV$Z)v^2e<&tH2BKB0I>k`caw;g#;_D4Y&QXz5O>~rr?#bK z<_=^|nVrWcl#r9Knl$3gn(#)TRV~efq8Qs6F_1%Gy!w zAZt(xuMNGPG@c!ag{0Hi~JHEu>OC_5a8P_UG6s7{M!MikYuIhEui>r4g} z)*87+2$sv>hZGB0o+~yL><#WfiN5} z;1m|cx<}K|yWUE{%n%kYUED9$z(X-j1n`teDzPb3g$tTRdEW5>V95^XD_AV2wY618 zwjl0gJi!qzx#SWWKYqNndbZhS8~0w}iUZ1M6G{C?J1 zXX$X_0S6qQKfNGcB7qevR?v$tzNlFckS|Dfyl6y;njk&$#ysi^y~EV%BhZ{G1Zi)W#@UTcpT{w<%n|gb~!q?7ETaBE3}V zn?R}d9Vy*Cf_gg8xAIcWj0n72F5g3~Bbq2&!?K^*wjEnYc61#DYnWTqbWqw_M=Bkn zeaC;FCf1%zD%{JH1Kv1i!k`Uk&_DppEpyWQ<-}uL;8Z!}kVE>(SOAC)q0)g|oz=|Y zsuV3USYo{Rj#(<^6Og@R$r3FKcG+bYeV^$d5CF2V$O}A2uqRD``xAp9Yz|Zu5E~eM zP$B9oETliba|#9OBdh>zPpjW7QD0jf6|u=5h&Hp5-F?(pQ>Upppyf6whrh8V zZn4Zxh1zN7&#tHO+a@)|=GTs)m!GVqOy?vDMtfO-ThEWg#N`2GevkX}*&@}{)iB!* zP=Uojl-Yk6PM6va8Zt3O<8}y8hTf%y)CBtctXrv+FKJ-{-#f+&vQ_;0;_5Zw?YH07 z)QaSN_`m@$v*0TifF_P{MR_rF8q;y&r=fctT`qSXNEzFdE$vnJSFKv5-J(xC@r3X7 zn6dlqx1Uz(4m#){op>PkLis6LS!RZ8cBzEcgk!M)J+bCT)SZ5wS#y+Lc%*^)R2LRprMKT;ww{Yq zl$EYfm^leF^RfwqH_PV>Zsmuz4s50%tCouvWlo;5g>|&gl%LU7(LGsI zq`4i35Tr6hC=W#O=%bHnod?b#i5UP8L%_0IeSLje`9&oNJY-vKwN*c4`hEAqR|R!{ z6o-koQInOdUAtCCy#D&vzv{(V{UiitJBr~U4#2ic7Td+FHY5TK!zLT6<>`DHMoNZ?c_^JYg%Ey~h-~;z$3Wo-Qbq zY#X!ou3e!j>_oH5lmYG~3&3kcv)m`e3aDMfa=(vFq#7DJs)0F0oOYjf6wMrU9Ay+= z6Snn41G2%fAgjU6q}0a?`|i81o&X{W&2hxI2nXGiRWlH721K;iZ#`HW&#yD6?#BuU zKst~saQ^w{(@%c#6Fs|ao9P+PwL=7je!**O8{Tz2r8;WJ%7y9eMSr1KZIoFtSm4GG zr`H8oDFdOR=ef-QrBjLy{@gSgKCG1|gPdiK-PQUrZ*_1CryN|m40=tIB4IM;a%9JTul;Pa2yYA{fLndpr zID;up{qSWvw#3~kHoR}nm<-f_tRN8R_qTbS*UbrK1cv}@Kgf8<9(2g{T2MqI5wYj_ zx6G&KU;ZbJAH5?ZTCt+iM>Y8b%hRj}u_i(`I$wls2Z9A1u1xoZS&r7yBM-gI6kbE? z)~{q+nctJO@Bln-LA&BTH~OJ%-L7WcXg`!>YG)C{_H>}8o9xDyXjQ3;+H)sUW2lM5 z?IQ1vJUXy#uwf`#UL**LzMhcP}AIXjFgq-fYG`fAgE)(9)$#wGSFIZ_hpV ztccFYiq4<)S>4KQ<|BjFQb6Xztc0qylew8%9qSbp}| zXIK0N3IyAM`x5rF1la-q`@jFI-EYW#M92}!7C`J}$LrMJ))rs4raVoVwlmN?!W)wB zP&ToGwx88X$+h+L@E=ned6AGg^AUJJcv1RazTF%g1+f`oEjmCGw;4)1?>UNAEnUWl zwlqtJ5~l1vvO}7zN(&A-Fn8DXwcb)qJc-b3nxPh*^>MHFAw^#hUBxE?ry9|B1Y z@SP)_2>uM%+M`F0rlXHOTHm85lD8q@>!31ko|xQ(G#^kc;-vx?UwpAP5!SC?uc;Wj z$vA_(k-T7f0l*mmxEAhDiDWrxZlYZ}{^w$m?NR2N@rDoRh@*C*o34DC?Jnn99XHl} zszhuk6xe9i=ulOVsm`K9j+#OZLp!xrzs>d*?KqQd=@(a0G|pOex#rDzcXEOp0cX;b zMZY5k_)DRfq2Uu7Y3A%EZg&H7gQ)hGrfjf& zOloA~z-|_0RLoE~oIpepmV}~K4NSH!<2Vq=#0Yy&nqYdV#Bl<1122QLA&D?yK#{*> z<}k_q0AXEl;lVeH4#G(Qd$9t5T5S>Tlx@`DnDs<*&g?TktN6DtZtC3f8 z`$>nL3Bz(ah|U%%m}kwkZY@3j);!A8uqX-SNacf++7PAn?~SJmzWqCH)S`yE7{!7t z1B1>B1JSUr;rs}PMPZ9DVgoeqYlqQpbFtcbq2x8$S+p+6yLAt|mGMz*RpSUm0Il6_$Ozx&&k!dAC+A5e_e>_cTJx;Ij9gXsY z3oDAUA{LG$%9S4*$HN?F(`51+JMY+f!E0apUDJq23}-(J80U0qSea`6v_`c z0Y%3t!sbsbJc6%bJ2#Z2tldq`!z0wWVFmq+?bRQhdn1){HY1KzBQ|M}w}m!CY?j61 z5J7RB!VCNS)Kd?lNz*da(p=BTV@g2eDj%V#J46{NUDVtAS$g*AHz^b^Qh_z^Xe`en zgOOaSWER1kuYh;TFR+~^xOYApLX4SM@px2+T|#bu!e?uO2iu|9%VMo z3d-5vK8adJ^wBm`8yEp$rmzuwG&@IJ(S*qfR;sq9FP(gZ*1lsrVG^-AibdHqAY3if*s25uQD`PosP!nCsM1G~D9kF$ogjdNoWZ1SMoVOho!QXg2x* zR7I}6Us8zRU?2#f0>ICsWg>N;qyl*fxC>#|EnmJ|JMa<5HtlT_5Q9cW1%mEy;yYe3 zTi3qXOu->$tx&jLTXQpY=Ko58NSM;a6j|Xs#R6ffvFqvCzbvP*O>>#ekE6HVdYM+0 zljmz{614UBDYVsA6KTk>FtztRP5bOOjuFeUJXou9z=-k`2q86r-^-5B+7J3@;cFe# z+_)XRxo|nHSlUjjSGKd(pQhS|FzqsXTiS8@WO{eyleFWmtgR0ZvIdc)Tp>kqMsy&W zpnPwHcH8QE)L>7dd@;@ah!G5q5Q~x+|1N322vEo#9ni3m0Sj-TnHP`oM4=)+B@4$J zuO5ifeOb568q?O3NDGh$>JyU-W)<9($ZTk9YtvdJ5)lAU@!|aufO!DL%j6W&VF_ly z45*lOY|{nD? zleeG2eN0d;izrx)o@3~V z7v3UjP0@@|BWRz4chG^Be7=`DyI0Yw_Sb06p*t~Kucv&WqysMT*a)f*)v}IIPc>sl zQca|pm5w}>vS>Y~U7^8%Gp_Q^sB95{IRyZ5VE+#xDvY~ihsZg^rm;8Xwbx$jhJTS} zjYJN3+;9%Ix7Us8f)u!2cG+d_VlOyQ*$6O;;83tGzvDnbnE)frJ4zjF#DO2rb|aSlrrRNCrMUD>s&C%0Cm z)fzt2rb_&oQcG1vEmv8!nvYfb$np11`#sgmpBc51&n@RxKH#=ctlp)Hxo(y2?o?LF zc5AKhXI_agVnR;euC*;`OuT|K%st+%&VzYD5IWelW#N%e!Mow zk<^9_N`#f*{`&g*eyqg&TQVuYL1aNUd4{HHgZj03hjhEUQdP?WlFqQYP zB9*2MIkt_N>hpy@rrsRewjJDdj;RqsGNk2ZDBxs$YMY%k=~5JNWA0ChxfVnQYxWT& z7!oo{Sxb(>B&lVpt>>eW8XJaCQ)B|wvs`b8jc4&Pg8P*uD+0=cGFjyQq}b*UF-Od6 z2a|Zkyo#521FMxW`LSLkB;0hT+Y zW)GAVYO;PWf=wg?Ad}4?JXn=!2g>#EDpAPN`kyO84bY{RlP9ttFM ziJZfdS%bG#IV004$a6efC9SzTWme8J>Z3N**3?mLq=}mAhH%qJawt)XHS46+MpkG& zPZ-k42X+Y_E^aQ{W2haK$YwRoex+ZNapsxkw(Xmp-oJd;%lKyLE)QC7V z5o(T?SKF?pnVVw{hYKbak_^2WJT9WaR`6w@poFObnu?pB{b%}by|dYUD4&Bn6dpD> zQII(y3RwlBb)sIZd{0mZJ4&_7>48_zWm9Mkg_wGvm0OV z$p8(KliuUFNS#H|qs%3Or5YNVm_!}#EBf&dujYrwslKI=T1G0G`I)gaew%s<*F`8* zWR4N&rmjMmHSjb|YdV*qV-W0u`arS&CRw0I@G^V};s>eVo|nZ|5C zgeFa{r&`wHo9nlsNh4>VXqGbBl1^7b`fq?0yt zd+kfJ!%oCe#|bHrZlbN4K10h_-cN2kRa2M67vr8ZTnavG=lkFI07b4uo{Cyq0URs0$P{)C(inj;IWBpP>E&>C$1p zdTL4Dul8K!IF2b|vCSG;M$f!+0|moTX4fS!Wmcr3EN7KQE?lA^T?mT`$&&Vit6+_u zF}8BvF&ymGRNmY$p6> zyFj$dt5I*N0Nx!p6VcTWU>4g|V-Io-HtZDRU)qsQdlft!D6>V)5K4dGbnzluLYR`7 zX`!qwzM5WJ_B3tiTE#lRKPXe`)By^9LdtuZ4r;4`h_cZnv_Ii6nOn2GOp; zB^M$y5m}2~0L2Xif<(Wi7JxXZ)AA}7kmGoHAlaKY<+UeuTuTS{vovI6jAG$tiUu1Q z$)lb8FkB0_rj1O}%C%U`wsEm#r=8>i3BDlOWCgXooK9u6^QSkHrRJta8a=9+ww^wg zLa`iEc7ZzIt);eia`e3}#fpt-S$)hFvrNS;gdB=kv{oOe zV@9~r30sEBwq{5t6*?vsCZ;M^ZWf)GGbV-wWzc5jt3WAMrrK6@vkBEr4b2gz?kKbB zEEQ5iY5y4)(b$^3bib0F9rWxI57I+VzDT`Go~1jVdzq5KCKfO@MT>dX9-X2B)T<$d z2&q2uo_=Y{gF>3&tB~;>H0zcb4^!wR9Gl zi}3JZB1j#UHz^-E-$! zqVYlrjbPiQAf&4lowOw7c;b?4JE<>UVkFm5OLLS;JOo37htiyB-zO`>rW_K8VnOCA zL3bx^WS69JO!aHtr>!SVB3O2)$nzr9W@^ZTG>wAX#+E$8%7nAf2q z$N-yyX@Lqr*^M2J6QmDeR{JS^NVUNlV{+c9#VS-hS5rBvIBNQ6VcV^=xa&`3MLU>s zoMkE;)*aAZG|BJ6cm0TfvjB}otTxO&)&vZ*a@mbS-Wnsduf2n^hyye?vn-BKDc?x@ z?{G0S6?Wq0GTQvIf{r)|qhvN3)swouu8vaaG#&Z*&(j0<-|vo>mx8MyALEA!@7)5? zi*T<+G!UXIelcf~*Bxmd!ZHL2IDiy!dLWfXkPASEfpj2QY-Q;fCIY-df@G;RlD51v z7eS3;wt=RNK9yQ%2Z}M7g(FRjz&KfqAR9T@N`fMiQ~_i#MV1&@#jyU=N|;^Jff5`# z*GoD1UOy1=Sybf_6CC zv4jIa6gAVYi+i!o2lN10aSvJ~94pd3+LYCPWVVNbC5KgJ>S(_mzD5n%Jt>!Ac^Il= zO**D+Yh_y=>p*F?tO#{X5G_TmnJaP>HyJ-&KabC#qs)b34LYJ#NY~JmktfikVTTjj zb`%V=Ee|$NPT$Z+shk8tpb?{P8Gw2#SS^D=wEQOTFJp&=d_>7Q$Ei z8=Zj4j|6|%%>oFnBO$?DhQVu&d40D6$dE+RzqyhECJV|&#gXDPP(Y{k@==9ShKhj= z^z`Zc(NZfbVL6KO zwek2+N+gCeO6n+R)i5z5&fLJi*Yosfq;Q~-Uy>1xd~B7_OF{ONt#r@KW!B|klNW=$QL)T+0>!^<1C3O#tIyej8q_SF|cWfFrjQHoj|hGO8bqk zA0YtA!loybm*hge8PbrDHvoV5V34~>L<@4v0|5xg8US7Z0C@_p7V?|mOu@^U@>|*g z09i=#M+dPQ9srdDaLS0{A&Ma4;bOO&l^zHT1OsvIhfT>8YsBf5)Y0<>b@#3#%1@+J z#~7xZoDO0n5>3?9FqUfLV;T9w*$k^=N^H<+KR6=H>LX!%ucLUhg%zkKJ{ommp-wf* zMyV#a7f*y?)Wz-hWtUSj-N`cE)@zz+F;W&ovX3;_v@E`Aax~Kl@f%<7SeYJ0j4uca z9xwoi0(I0V2pFt6Ru$3G0N4%kl<+rLhD2Y)?FM-&<)Gdd@^hZZ_Ix6fO|BsbMYKye z1!}uM@wi3;_%$_kG<@hNUA&&jBq^KkrF3rI=$LJGR@|RwU*gz2UBW+hO|U! z^ym?6?=~`W!e!sN(&apsuJj1%l;I2?>(+MZ^r2XE2-VlLP+gdhSPM1AN7IB+)2SYX zZ`{>&ZR_delm3go``vTtzyl9(-}#zruF-3_06^Jr4*B^Tv{fKWMAAkZoV4z-F;eHG zK$r8~RsV|BYJPG*s9FdwviS7VPp8kbJ$U7nSL#A8?*eI68|L5PCr6g3EO_dD29PbX z3o{f>djOVH#L|i;>%(_-5&i_dP8sBS31QZd<9IIPEKS|i-q(q|U}}xC2x0N! zLP6u2vN21CVuQN!v(k@0SlHw`V!A&*|@w-LhGVyZ$JTNC+Vb8x-Bc^1FvJ*kJ6)X^qOuSuex&+SHv zahBN*`vv0hJsPReGPkogeNcLB z(#=)E1O~ObubHtZVBTi36+wWXE7Y7AavlREwzI5UZ^o{%90rmY05C6*XC~A$cI;NP zX6^fwVk(YAY8+p9&{>K-S7JLCEBsjnU5118g0R|9Nmn{5*07^aL7om%hP2BQq0M+W zuE({=IEgt9H?94{e1iOy9eofRM%gO>)QtdxcdWpku~dg$P;3c==)Jz>^rQQ(p{LU? z=^X^(wK0B9fr=59*Fi;*68FEvN?f5tsy9!2joXzjKJr}JDm+GK%ur}x+xtTyx>^HX z*P9?23Fa{Du_B!EbhxJw46;YDv{YXzsbkec8e8;wV-Qu6+5?u1r5$B5Ls5iQ z41lsEN~-zJXhUJ;{AGWpOYi*|sU}vq8f%z^6V>MVKvH2;Q98mC1gj|V z%O_pJd*7lz(>ETzgrZx=C{fH)wAf4`J4o3ua~Q0`#G(b+tjS)URIwPv**r`KauilM zDkZ{XZ79$mkGz8R9KV;&e1#((rA4STQG;dYY7pSvtwkyiuqSgY7IP~FWa~i_ux*dI ziMH$ngYA284bZ|TiOv;dnr73Cn< zj8p*SHAf$Pbj9x|_hBWgVo+sUJ|N%OAJRcGHV~EjWY0%)f=T%gQ+!2x^XARd=UEHZ zf6MjQTN39Vd+af{k0^zaX8_Dy>+SL;V&?nqx8Hsh*PG=M(P|je%$w(^DYwm0+y8!lb>J(C1G&=_LKPcXvJWTHGt`n7iwF zLHcBFILi8gK8M;W0gQ|ERh1doy~`d_Q?A-wjk|4cwasn&sVTS2RongH5Vidu4p1}y z@EJ8}{+?>;FZNdd>~MCgUy*!I9rw`rYWLqCr+)LuZ&b>55dQ1G->O=F&(~hLxBBaf zCz(+cRI$8=r@YgqD0{PXb;xOyyNW3U#S1ZJanAuHf(7M@+t(F;%;L3-h2sRK2h1&i z*If$v40$L~FIf~vyhH%Va{y!`Qe(wJfG0|P?PjjHGD53vV1xk~GXk<6sQ{vA2p2-; zN_8XtmNiBsG9}rq_#HlI^d%c$Wh;!rT%y}vx}OTHOD6Ixcd=$ERwj<6%O-S5=co;< zX{8xMrtvtGki$W_J$ z1Dl0t3>X|1STY#CZ^V$5p`5{o4W~X3Aj?t-U{H{~hyl6fmRq!=!Q6HM0}8ELlpfgA zlBA&Hy?kJ~XxW5R23zcXX#E1_LOCV^%@QGK^YC2B&g;E=(D#6&ZF+OjsbBvVM= zByQ|1>>R@C0=6NnhX70vbMf$tF1kp6B61}F=K?y6^d$iHp+Fnn5*_R*B_Q4>0Ytd~FssZwG*z}3 zynX;sAtFu)txDFZ3>w0MaQ%XUg$v*}_?eL%i}0hI6VMZgoUJ?9K&O@KR_V1+PzZoE zfWvlC@19pk@tAcH6|y!J!ZzhXx}F5Bv6&p8DBFEu{(YC+F7(YKoORy*vU&lVXaVi$ zQI<`sc>)C_cus?}qX&g(X-E)ao;N85Y#e|DH~_L9VP|5ccmY&v0#Kp@5M=JN&pz%o zFqibJduFQyJ7lt$*GT!Oq{NyDiinzF5j+!_@-E&GqHN-Vgh+9In5j%azHEphY~7uZ z0cU|re0HS!oa7AKV*9*Av7AjUeNj4n*3op=sn^igcmD!4^sv33vdGD9D=?J?$WG?y zEBhW#4}9gfw0*dRmRTRrBTwH+jWyV~#p!EbU(#^^Yz9=_KiUMpP#Qog`vj2G2SAhx ztPvCdrv_H)7eINgc`e$&WDpl8C{y;B@Y;ueia6xAeNi!1L z>g$#@ZlIhhP~K%Un3y(;;Tqa+`X0;!6urLkuk_uguBJ|_jcxrpy%i?J~}sRzh?p+ak7fBe%;t;hvvm6j*U;>tvsme~-F3{QZ`I(# zdB}YL1A~1j;jx0E1{rE183@I00S*Z9_p?AU0_Q+702vqpvgD*GPl4peAAejUN|Yco zX%OF`0Ge{soCJ{9@Jj>908%>5}v=zwuf9{6`2xQy;vJfDKCNs6${ZY(&HIE;!+!mq`|B!YE{&I7s$OVDN*FjA$=qfNf)N!A9{LkPDLwR11GFC_#esP`D%s z0qHmZZ-yxV%Q+yCP!ukCp8Sq{D(rKKy*a#y^A_<*yWZ%Vx%x2f!FwP|1~bn4o;XnF zjD!HQAG!tvo22n$hSUwEF)b5FWm&Th)=`W8wM5JFtTVS*l#M$9k5W)pez3G(97_%c z!{vqXFyTW1teVM68$Y)YrkN9V=1EbnSL`syeN(VS5G`6Q1`k><(p_Y`BOn)n3&>rR z)ru^}d08$ItQD)!n_Ke%L{=h#1%EmsSMp-!3(5wjNF6hcSu*fX_M%j;SeIWhC za>w|N<*-CN2)h=JgNZ87^QIu1rlo7IhaC*mF*^3zdUpyZZ8nCIv?vMPBt&P=J%bv1 zMp5Zqn}(!X>C{8MqOV`vv4(oA99cRYT{%k&n+GCn=>W}{xEICs>J33BQDKmX{>U=g z1n;(d)vm!H!$gBp<=utVyw^*S-fODKX9G#aX%G;BaHaVSlcG%1M;sW!3GfMh0SmavzRo5{lA{SSQKip*%WcSY z_zih=aPELpA&EZ*5smHi7*XSC2%R?&?)whA(>s?EkxJ`+M zTAiqXB=mrOhazj>+(w>T%oXTMBMzZUkGzP2lHQJ8eF6jL?V0dzMYMn*%nxzq?_jw| z5`-5Gju!9rvJ(J6;?;wBt^igMkkt#lo0S8qQ8Pd$5zIvB5AffEOIbPrt(+JTaJWJt zsA%5&@6e0sS14c4)LM)=RU!Plg~ELmrl^WA6JP?cluke_F-eE5IH~m|+Iz|_bnTHp zaqMQT+9R1pRhU+rf{z8!g8?s<`<3j<*1WP9&gOzywkwq)CREFd^G;N`Mr-cKtPu?z zlx<-vrV`6#i>`k57W(6}_fX#`)Qcx{;($&c3OGo|;_%otvjl`=mH)LxI&a^3^wqtN z)tWqzi~U=)S*6@M!9l^GJY)ZE5P5CNb&;Fqz#|11fGkz-{nfxUlT1g9W0Seo3 zY6#TRf#VONGfw#uZ5?R^7kA95K)DQa8#}5z%L-ju*WoD(vrbL1X zC;@^(C6yuOHG*LL2G^^YjOGF{UN2g}e3t$ebn3c5rQGg_hM{!T5tmSx>ZV0)@6gir z6|`U(!B6R&_uIE5KyuLeWS= zhUZPvdrfXrt$%*MsWO!p`4I?aV13@ajjfYQd}LS*XZgtJ9p({}wy{qKlW|ZdIDWY8 z3f=Mg(06`9!29y%-?Czl#K}$bNG6B`CnT`qZu=0Bt&J_c)DyB~P!?-uvwo<)^1p;# z;DZ%GmdlZV%{FtGHS8MxOgPn@6w_|clCy#qZFWr32R@N9dazJ!hRb>EKm(BUCNISS zi3m=>76-KoCon-`&Qc01duRKtpt~v9*tVnTyS*{YVb^2|kS@bxrI+4 z2vthG7cxlzrd=!=GR~pySa#d<=CJ{QWb8HoV-iP>!sjA`6-Ov3XpJ zVnFODC+9@=o92?N5oWIKHIPH)lLr5hh!*6?jso7jJIw2lr3#N0*tdDF7i8}c*-zx7 zC`KO#{6`{MKoa|eqD>HG%4F48JEU5WKyuLlvhu^H2|m@zSTz8(nfrst4mExt$%+6d zZ?Z^?38IXlt_*U2{P^)7O6&eO;S=lY{xIOp2NT;`iq(~2i<$|5@QwkQ$YPbmL{kIz zwmaCRlRruDABSiGoh_M8>+i^8QyzzdC|o#)oHJaDBnYK8E;Qo}Q+n^0=X@IAKMK)a z&zB%q&U+OoLAH3nOl{k|exM=xv?yi&QHcI9;1`Pgv>buf` 。 + +`size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob)` + +| 参数 | 描述 | +| ---- | -------------------------- | +| db | 数据库对象 | +| blob | blob 对象 | +| 返回 | 实际读取到的 blob 数据长度 | + +## KVDB + +### 初始化 KVDB + +`fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *part_name, struct fdb_default_kv *default_kv, void *user_data)` + +| 参数 | 描述 | +| ---------- | -------------------------------------------------------- | +| db | 数据库对象 | +| name | 数据库名称 | +| part_name | 使用 FAL 分区表中的哪一个分区 | +| default_kv | 默认 KV 集合,第一次初始化时,将会把默认 KV 写入数据库中 | +| user_data | 用户自定义数据,没有时传入 NULL | +| 返回 | 错误码 | + +### 控制 KVDB + +通过命令控制字,用户可以对数据库进行一些控制操作 + +`void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg)` + +| 参数 | 描述 | +| ---- | ---------- | +| db | 数据库对象 | +| cmd | 命令控制字 | +| arg | 控制的参数 | +| 返回 | 错误码 | + +支持的命令控制字如下: + +```C +#define FDB_KVDB_CTRL_SET_SEC_SIZE 0x0 /**< 设置扇区大小 */ +#define FDB_KVDB_CTRL_GET_SEC_SIZE 0x1 /**< 获取扇区大小 */ +#define FDB_KVDB_CTRL_SET_LOCK 0x2 /**< 设置加锁函数 */ +#define FDB_KVDB_CTRL_SET_UNLOCK 0x3 /**< 设置解锁函数 */ +``` + +#### 扇区大小与块大小 + +FlashDB 内部存储结构由 N 个扇区组成,每次格式化时是以扇区作为最小单位。而一个扇区通常是 Flash 块大小的 N 倍,比如: Nor Flash 的块大小一般为 4096。 + +默认 KVDB 会使用 1倍 的块大小作为扇区大小,即:4096。此时,该 KVDB 无法存入超过 4096 长度的 KV 。如果想要存入比如:10K 长度的 KV ,可以通过 control 函数,设置扇区大小为 12K,或者更大大小即可。 + +### 设置 KV + +使用此方法可以实现对 KV 的增加和修改功能。 + +- **增加** :当 KVDB 中不存在该名称的 KV 时,则会执行新增操作; +- **修改** :入参中的 KV 名称在当前 KVDB 中存在,则把该 KV 值修改为入参中的值; + +通过 KV 的名字来获取其对应的值。支持两种接口 + +#### 设置 blob 类型KV + +`fdb_err_t fdb_kv_set_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)` + +| 参数 | 描述 | +| ---- | ---------------------------- | +| db | 数据库对象 | +| key | KV 的名称 | +| blob | blob 对象,做为 KV 的 value | +| 返回 | 错误码 | + +示例: + +```C +struct fdb_blob blob; +int temp_data = 36; +/* 通过 fdb_blob_make 构造 blob 对象,作为 "temp" KV 的 value */ +fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); +``` + +#### 设置 string 类型KV + +`fdb_err_t fdb_kv_set(fdb_kvdb_t db, const char *key, const char *value)` + +| 参数 | 描述 | +| ----- | ----------- | +| db | 数据库对象 | +| key | KV 的名称 | +| value | KV 的 value | +| 返回 | 错误码 | + +### 获取 KV + +#### 获取 blob 类型 KV + +`size_t fdb_kv_get_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob)` + +| 参数 | 描述 | +| ---- | ------------------------------------- | +| db | 数据库对象 | +| key | KV 的名称 | +| blob | 通过 blob 对象,返回 KV 的 blob value | +| 返回 | 错误码 | + +示例: + +```C +struct fdb_blob blob; +int temp_data = 0; +/* 构造 blob 对象,用于存储 get 回来的 "temp" KV 的 value 数据 */ +fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); +/* 如果有需要,可以检查 blob.saved.len 是否大于 0 ,确保 get 成功 */ +if (blob.saved.len > 0) { + FDB_INFO("get the 'temp' value is: %d\n", temp_data); +} +``` + +#### 获取 KV 对象 + +与 `fdb_kv_get_blob` API 不同,该 API 在 get 过程中并不会执行 value 数据的读取动作。返回的 KV 对象中存放了读取回来的 KV 属性,该 API 适用于 value 长度不确定,或 value 长度过长,需要分段读取的场景。 + +`fdb_kv_t fdb_kv_get_obj(fdb_kvdb_t db, const char *key, fdb_kv_t kv)` + +| 参数 | 描述 | +| ---- | ------------------------------------------------------------ | +| db | 数据库对象 | +| key | KV 的名称 | +| kv | 通过 KV 对象,返回 KV 的属性,可以再用 `fdb_kv_to_blob` 转换为 blob 对象,再进行数据读取 | +| 返回 | 错误码 | + +#### 获取字符串类型 KV + +**注意** : + +- 该函数不允许连续使用,使用时需使用 strdup 包裹,确保每次返回回来的字符串内存空间独立; +- 该函数不支持可重入,返回的值位于函数内部缓冲区,出于安全考虑,请加锁保护。 + +`char *fdb_kv_get(fdb_kvdb_t db, const char *key)` + +| 参数 | 描述 | +| ---- | ----------------------------------- | +| db | 数据库对象 | +| key | KV 的名称 | +| 返回 | !=NULL: KV 的 value;NULL: 获取失败 | + +### 删除 KV + +`fdb_err_t fdb_kv_del(fdb_kvdb_t db, const char *key)` + +| 参数 | 描述 | +| ---- | ---------- | +| db | 数据库对象 | +| key | KV 的名称 | +| 返回 | 错误码 | + +### 重置 KVDB + +将 KVDB 中的 KV 重置为初始时的默认值 + +`fdb_err_t fdb_kv_set_default(fdb_kvdb_t db)` + +| 参数 | 描述 | +| ---- | ---------- | +| db | 数据库对象 | +| 返回 | 错误码 | + +### 打印 KVDB 中的 KV 信息 + +`void fdb_kv_print(fdb_kvdb_t db)` + +| 参数 | 描述 | +| ---- | ---------- | +| db | 数据库对象 | +| 返回 | 错误码 | + +### KV 对象转换为 blob 对象 + +`fdb_blob_t fdb_kv_to_blob(fdb_kv_t kv, fdb_blob_t blob)` + +| 参数 | 描述 | +| ---- | ------------------ | +| kv | 待转换的 KV 对象 | +| blob | 转换前的 blob 对象 | +| 返回 | 转换后的 blob 对象 | + +### 初始化 KV 迭代器 +`fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kv_iterator_t itr)` + +| 参数 | 描述 | +| ---- | -------------------- | +| itr | 待初始化的迭代器对象 | +| 返回 | 初始化后的迭代器对象 | + +### 迭代 KV + +使用该迭代器 API,可以遍历整个 KVDB 中的所有 KV。 + +> **注意**:使用前请先初始化迭代器 + +`bool fdb_kv_iterate(fdb_kvdb_t db, fdb_kv_iterator_t itr)` + +[点击查看示例](zh-cn/sample-kvdb-traversal.md) + +## TSDB + +### 初始化 TSDB + +`fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *part_name, fdb_get_time get_time, size_t max_len, void *user_data)` + +| 参数 | 描述 | +| --------- | ------------------------------- | +| db | 数据库对象 | +| name | 数据库名称 | +| part_name | 使用 FAL 分区表中的哪一个分区 | +| get_time | 获取当前时间戳的函数 | +| max_len | 每条 TSL 的最大长度 | +| user_data | 用户自定义数据,没有时传入 NULL | +| 返回 | 错误码 | + +### 控制 TSDB + +通过命令控制字,用户可以对数据库进行一些控制操作 + +`void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg)` + +| 参数 | 描述 | +| ---- | ---------- | +| db | 数据库对象 | +| cmd | 命令控制字 | +| arg | 控制的参数 | +| 返回 | 错误码 | + +支持的命令控制字如下: + +```C +#define FDB_TSDB_CTRL_SET_SEC_SIZE 0x0 /**< 设置扇区大小 */ +#define FDB_TSDB_CTRL_GET_SEC_SIZE 0x1 /**< 获取扇区大小 */ +#define FDB_TSDB_CTRL_SET_LOCK 0x2 /**< 设置加锁函数 */ +#define FDB_TSDB_CTRL_SET_UNLOCK 0x3 /**< 设置解锁函数 */ +#define FDB_TSDB_CTRL_SET_ROLLOVER 0x4 /**< 设置是否滚动写入,默认滚动。设置非滚动时,如果数据库写满,无法再追加写入 */ +#define FDB_TSDB_CTRL_GET_ROLLOVER 0x5 /**< 获取是否滚动写入 */ +#define FDB_TSDB_CTRL_GET_LAST_TIME 0x6 /**< 获取上次追加 TSL 时的时间戳 */ +``` + +### 追加 TSL + +对于 TSDB ,新增 TSL 的过程,就是往 TSDB 末尾追加新 TSL 的过程 + +`fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob)` + +| 参数 | 描述 | +| ---- | --------------------------- | +| db | 数据库对象 | +| blob | blob 对象,做为 TSL 的数据 | +| 返回 | 错误码 | + +### 迭代 TSL + +遍历整个 TSDB 并执行迭代回调 + +`void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg)` + +| 参数 | 描述 | +| ------ | --------------------------------------- | +| db | 数据库对象 | +| cb | 回调函数,每次遍历到 TSL 时会执行该回调 | +| cb_arg | 回调函数的参数 | +| 返回 | 错误码 | + +### 按时间段迭代 TSL + +按时间段范围,遍历整个 TSDB 并执行迭代回调 + +`void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg)` + +| 参数 | 描述 | +| ------ | --------------------------------------- | +| db | 数据库对象 | +| from | 开始时间戳 | +| to | 结束时间戳 | +| cb | 回调函数,每次遍历到 TSL 时会执行该回调 | +| cb_arg | 回调函数的参数 | +| 返回 | 错误码 | + +### 查询 TSL 的数量 + +按照传入的时间段,查询符合状态的 TSL 数量 +`size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status)` + +| 参数 | 描述 | +| ------ | -------------- | +| db | 数据库对象 | +| from | 开始时间戳 | +| to | 结束时间戳 | +| status | TSL 的状态条件 | +| 返回 | 数量 | + +### 设置 TSL 状态 + +`fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status)` + +| 参数 | 描述 | +| ------ | ------------ | +| db | 数据库对象 | +| tsl | TSL 对象 | +| status | TSL 的新状态 | +| 返回 | 错误码 | + +### 清空 TSDB + +`void fdb_tsl_clean(fdb_tsdb_t db)` + +| 参数 | 描述 | +| ---- | ---------- | +| db | 数据库对象 | +| 返回 | 错误码 | + +### TSL 对象转换为 blob 对象 + +`fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob)` + +| 参数 | 描述 | +| ---- | ------------------ | +| tsl | 待转换的 TSL 对象 | +| blob | 转换前的 blob 对象 | +| 返回 | 转换后的 blob 对象 | \ No newline at end of file diff --git a/docs/zh-cn/configuration.md b/docs/zh-cn/configuration.md new file mode 100644 index 0000000..169eb22 --- /dev/null +++ b/docs/zh-cn/configuration.md @@ -0,0 +1,41 @@ +# 配置说明 + +FlashDB 的使用时,可以通过 fdb_cfg.h 对其进行功能配置,该文件模板位于 `inc` 目录下,也可以去具体的 demo 工程里拷贝。下面详细介绍一下配置详情 + +## FDB_USING_KVDB + +使能 KVDB 功能 + +### FDB_KV_AUTO_UPDATE + +使能 KV 自动升级功能。该功能使能后, `fdb_kvdb.ver_num` 存储了当前数据库的版本,如果版本发生变化时,会自动触发升级动作,将更新新的默认 KV 集合至当前数据库中。 + +## FDB_USING_TSDB + +使能 TSDB 功能 + +## FDB_WRITE_GRAN + +Flash 写粒度,单位为 bit。目前支持 + +- 1: nor flash +- 8: stm32f2/f4 片上 Flash +- 32: stm32f1 片上 Flash + +如果数据库中使用了多种 Flash 规格,例如:既有 nor flash,也有 stm32f4 片上 Flash ,此时取最大值作为配置项,即:8 bit + +## FDB_BIG_ENDIAN + +MCU 大小端配置,默认不配置时,系统自动使用小端配置 + +## FDB_PRINT(...) + +打印函数宏定义配置,默认不配置时,使用 printf 作为打印日志是输出函数。用户也可以自定义新的打印函数宏定义,例如: + +```C +#define FDB_PRINT(...) my_printf(__VA_ARGS__) +``` + +## FDB_DEBUG_ENABLE + +使能调试信息输出。关闭该配置时,系统将不会输出用于调试的日志。 \ No newline at end of file diff --git a/docs/zh-cn/demo-details.md b/docs/zh-cn/demo-details.md new file mode 100644 index 0000000..bfb1873 --- /dev/null +++ b/docs/zh-cn/demo-details.md @@ -0,0 +1,209 @@ +# 演示工程详细说明 + +## 功能代码说明 + +演示工程主要演示了 FlashDB 从初始化,到示例运行的过程。 + +### main.c + +演示工程中,`main.c` 中的 `main 函数` 为入口函数,该函数分为两段,分别初始化了一个 KVDB 和 TSDB 对象,然后执行与之对应的示例函数,大致内容如下: + +```C +#ifdef FDB_USING_KVDB + { /* KVDB Sample */ + struct fdb_default_kv default_kv; + + default_kv.kvs = default_kv_table; + default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]); + /* set the lock and unlock function if you want */ + fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, lock); + fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, unlock); + /* Key-Value database initialization + * + * &kvdb: database object + * "env": database name + * "fdb_kvdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table. + * Please change to YOUR partition name. + * &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully. + * NULL: The user data if you need, now is empty. + */ + result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, NULL); + + if (result != FDB_NO_ERR) { + return -1; + } + + /* run basic KV samples */ + kvdb_basic_sample(&kvdb); + /* run string KV samples */ + kvdb_type_string_sample(&kvdb); + /* run blob KV samples */ + kvdb_type_blob_sample(&kvdb); + } +#endif /* FDB_USING_KVDB */ + +#ifdef FDB_USING_TSDB + { /* TSDB Sample */ + /* set the lock and unlock function if you want */ + fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, lock); + fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, unlock); + /* Time series database initialization + * + * &tsdb: database object + * "log": database name + * "fdb_tsdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table. + * Please change to YOUR partition name. + * get_time: The get current timestamp function. + * 128: maximum length of each log + * NULL: The user data if you need, now is empty. + */ + result = fdb_tsdb_init(&tsdb, "log", "fdb_tsdb1", get_time, 128, NULL); + /* read last saved time for simulated timestamp */ + fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_GET_LAST_TIME, &counts); + + if (result != FDB_NO_ERR) { + return -1; + } + + /* run TSDB sample */ + tsdb_sample(&tsdb); + } +#endif /* FDB_USING_TSDB */ +``` + +#### 设置加锁与解锁 + +初始化 KVDB 及 TSDB 前通常需要通过 `control` 函数设置 `加锁回调` 与 `解锁回调` : + +- KVDB: + - fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, lock); + - fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, unlock); +- TSDB: + - fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, lock); + - fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, unlock); + +对于裸机平台,加锁与解锁回调通常设置为关中断与开中断函数。而 RTOS 平台一般使用 mutex 互斥锁或 二值信号量 的 take 及 release 动作作为加锁与解锁的方式。 + +#### 模拟时间戳 + +对于 TSDB,正常项目中的时间戳应当通过 RTC 或者网络时钟方式来获取,但是这里为了增强演示工程的通用性,使用 `fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_GET_LAST_TIME, &counts);` 获取 TSDB 上次使用的时间戳,存入 `counts` 。每次使用 `get_time` 获取当前时间时,会对 `counts` 进行加一处理,模拟时间往前走的动作,避免重复。 + +所以,使用这种方法模拟的时间戳没有实时时间的含义,只是为了让每条记录插入的时间戳不重复。 + +#### 示例 + +本文主要讲解初始化相关代码功能,更多关于示例函数的详细讲解,请阅读对应的示例详解。 + +| 示例函数 | 说明 | 详解 | +| ------------------------------ | ------------------ | ----------------------------------------- | +| kvdb_basic_sample(&kvdb) | KVDB 基础示例 | [点击查看](zh-cn/sample-kvdb-basic) | +| kvdb_type_string_sample(&kvdb) | 字符串类型 KV 示例 | [点击查看](zh-cn/sample-kvdb-type-string) | +| kvdb_type_blob_sample(&kvdb) | blob 类型 KV 示例 | [点击查看](zh-cn/sample-kvdb-type-blob) | +| tsdb_sample(&tsdb) | TSDB 基础示例 | [点击查看](zh-cn/sample-tsdb-basic) | + +## 首次运行日志 + +下面将对日志进行分段讲解。 + +### FAL 初始化 + +FAL 初始化时会打印 Flash 设备信息及分区表信息。 + +``` +[D/FAL] (fal_flash_init:65) Flash device | stm32_onchip | addr: 0x08000000 | len: 0x00040000 | blk_size: 0x00000800 |initialized finish. +[I/FAL] ==================== FAL partition table ==================== +[I/FAL] | name | flash_dev | offset | length | +[I/FAL] ------------------------------------------------------------- +[I/FAL] | fdb_tsdb1 | stm32_onchip | 0x0001a000 | 0x00002000 | +[I/FAL] | fdb_kvdb1 | stm32_onchip | 0x0001c000 | 0x00004000 | +[I/FAL] ============================================================= +[I/FAL] Flash Abstraction Layer (V0.5.0) initialize success. +``` + +### KVDB 初始化 + +KVDB 每次初始化时会检查扇区头部信息是否正确(扇区头部存储了一些属性信息),如果不正确,将会自动格式化该扇区。 + +Flash 首次使用时,通常需要格式化处理,所以首次初始化时的日志会含有格式化的信息。格式化成功后,后续每次初始化无需再次格式化。 + +``` +[FlashDB][kv][env] (src/fdb_kvdb.c:1599) KVDB in partition fdb_kvdb1, size is 16384 bytes. +[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00000000). +[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00000800). +[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00001000). +[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00001800). +[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00002000). +[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00002800). +[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00003000). +[FlashDB][kv][env] Sector header info is incorrect. Auto format this sector (0x00003800). +[FlashDB][kv][env] All sector header is incorrect. Set it to default. +[FlashDB] FlashDB V1.0.0 beta is initialize success. +[FlashDB] You can get the latest version on https://github.com/armink/FlashDB . +``` + +### TSDB 初始化 + +与 KVDB 类似,TSDB 首次初始化时,也会自动执行格式化动作。 + +``` +[FlashDB][tsl][log] Sector (0x00000000) header info is incorrect. +[FlashDB][tsl][log] All sector format finished. +[FlashDB][tsl][log] (src/fdb_tsdb.c:759) TSDB (log) oldest sectors is 0x00000000, current using sector is 0x00000000. +``` + +### 运行示例 + +日志详解,请阅读 [示例文档](zh-cn/sample-kvdb-basic) + +``` +[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ==================== +[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 0 +[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 1 +[FlashDB][sample][kvdb][basic] =========================================================== +[FlashDB][sample][kvdb][string] ==================== kvdb_type_string_sample ==================== +[FlashDB][sample][kvdb][string] create the 'temp' string KV, value is: 36C +[FlashDB][sample][kvdb][string] get the 'temp' value is: 36C +[FlashDB][sample][kvdb][string] set 'temp' value to 38C +[FlashDB][sample][kvdb][string] delete the 'temp' finish +[FlashDB][sample][kvdb][string] =========================================================== +[FlashDB][sample][kvdb][blob] ==================== kvdb_type_blob_sample ==================== +[FlashDB][sample][kvdb][blob] create the 'temp' blob KV, value is: 36 +[FlashDB][sample][kvdb][blob] get the 'temp' value is: 36 +[FlashDB][sample][kvdb][blob] set 'temp' value to 38 +[FlashDB][sample][kvdb][blob] delete the 'temp' finish +[FlashDB][sample][kvdb][blob] =========================================================== +省略 TSDB 初始化日志…… +[FlashDB][sample][tsdb] ==================== tsdb_sample ==================== +[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85) +[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90) +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] query count is: 2 +[FlashDB][sample][tsdb] set the TSL (time 1) status from 2 to 3 +[FlashDB][sample][tsdb] set the TSL (time 2) status from 2 to 3 +[FlashDB][sample][tsdb] =========================================================== +``` + +## 二次运行日志 + +这里主要看下 KVDB 与 TSDB 初始化的日志,对比首次初始化可以发现,第二次启动时的初始化日志会相对少很多,主要少了格式化的过程。 + +``` +[D/FAL] (fal_flash_init:65) Flash device | stm32_onchip | addr: 0x08000000 | len: 0x00040000 | blk_size: 0x00000800 |initialized finish. +[I/FAL] ==================== FAL partition table ==================== +[I/FAL] | name | flash_dev | offset | length | +[I/FAL] ------------------------------------------------------------- +[I/FAL] | fdb_tsdb1 | stm32_onchip | 0x0001a000 | 0x00002000 | +[I/FAL] | fdb_kvdb1 | stm32_onchip | 0x0001c000 | 0x00004000 | +[I/FAL] ============================================================= +[I/FAL] Flash Abstraction Layer (V0.5.0) initialize success. +[FlashDB][kv][env] (D:/Program/STM32/FlashDB/src/fdb_kvdb.c:1599) KVDB in partition fdb_kvdb1, size is 16384 bytes. +[FlashDB] FlashDB V1.0.0 beta is initialize success. +[FlashDB] You can get the latest version on https://github.com/armink/FlashDB . +省略示例运行日志…… +[FlashDB][tsl][log] (D:/Program/STM32/FlashDB/src/fdb_tsdb.c:759) TSDB (log) oldest sectors is 0x00000000, current using sector is 0x00000000. +省略示例运行日志…… +``` + diff --git a/docs/zh-cn/demo-stm32f103ve.md b/docs/zh-cn/demo-stm32f103ve.md new file mode 100644 index 0000000..d2261a4 --- /dev/null +++ b/docs/zh-cn/demo-stm32f103ve.md @@ -0,0 +1,36 @@ +# 基于 STM32F10X 片上 flash 的演示 + +## 介绍 + +基于 STM32F10X 芯片片上 Flash 的 KVDB 及 TSDB 演示 + +- MCU: STM32F103VE +- Flash Driver + - STM32 on-chip flash +- IO + - UART + - TXD: PA9 + - RXD: PA10 + +## 使用 + +### 步骤1:连接串口助手或终端 + +将开发板的串口连接至 PC,并打开串口助手或终端工具 + +### 步骤2:打开演示工程 + +支持下面两种 IDE + +- Keil MDK:打开 `RVMDK\FlashDB.uvprojx` +- [RT-Studio](https://www.rt-thread.org/page/studio.html): 通过工程向导进行导入 + +### 步骤3:编译与下载 + +编译演示工程,如果编译成功,下载固件至开发板 + +### 步骤4:检查日志结果 + +演示工程的运行结果,将会输出至 PC 的串口工具上。 + +> PS: 演示工程代码及日志分析,详见:入门 -> [演示说明文档](zh-cn/demo-details.md) \ No newline at end of file diff --git a/docs/zh-cn/demo-stm32f405rg-spi-flash.md b/docs/zh-cn/demo-stm32f405rg-spi-flash.md new file mode 100644 index 0000000..8e3b943 --- /dev/null +++ b/docs/zh-cn/demo-stm32f405rg-spi-flash.md @@ -0,0 +1,42 @@ +# 基于 STM32F40X 片外 SPI-Flash 的演示 + +## 介绍 + +基于 STM32F40X 芯片片上 Flash 的 KVDB 及 TSDB 演示 + +- MCU: STM32F405RG +- Flash Driver + - spi flash (W25Q64) by using [SFUD](https://github.com/armink/SFUD) +- IO + - UART + - TXD: PA9 + - RXD: PA10 + - SPI + - CS: PB12 (software CS) + - SCK: PB13 + - MISO: PB14 + - MOSI: PB15 + +## 使用 + +### 步骤1:连接串口助手或终端 + +将开发板的串口连接至 PC,并打开串口助手或终端工具 + +### 步骤2:打开演示工程 + +支持下面两种 IDE + +- Keil MDK:打开 `RVMDK\FlashDB.uvprojx` +- [RT-Studio](https://www.rt-thread.org/page/studio.html): 通过工程向导进行导入 + +### 步骤3:编译与下载 + +编译演示工程,如果编译成功,下载固件至开发板 + +### 步骤4:检查日志结果 + +演示工程的运行结果,将会输出至 PC 的串口工具上。 + +> PS: 演示工程代码及日志分析,详见:入门 -> [演示说明文档](zh-cn/demo-details.md) + diff --git a/docs/zh-cn/demo-stm32f405rg.md b/docs/zh-cn/demo-stm32f405rg.md new file mode 100644 index 0000000..59de117 --- /dev/null +++ b/docs/zh-cn/demo-stm32f405rg.md @@ -0,0 +1,37 @@ +# 基于 STM32F40X 片上 flash 的演示 + +## 介绍 + +基于 STM32F40X 芯片片上 Flash 的 KVDB 及 TSDB 演示 + +- MCU: STM32F405RG +- Flash Driver + - STM32 on-chip flash +- IO + - UART + - TXD: PA9 + - RXD: PA10 + +## 使用 + +### 步骤1:连接串口助手或终端 + +将开发板的串口连接至 PC,并打开串口助手或终端工具 + +### 步骤2:打开演示工程 + +支持下面两种 IDE + +- Keil MDK:打开 `RVMDK\FlashDB.uvprojx` +- [RT-Studio](https://www.rt-thread.org/page/studio.html): 通过工程向导进行导入 + +### 步骤3:编译与下载 + +编译演示工程,如果编译成功,下载固件至开发板 + +### 步骤4:检查日志结果 + +演示工程的运行结果,将会输出至 PC 的串口工具上。 + +> PS: 演示工程代码及日志分析,详见:入门 -> [演示说明文档](zh-cn/demo-details.md) + diff --git a/docs/zh-cn/porting.md b/docs/zh-cn/porting.md new file mode 100644 index 0000000..5dc60d8 --- /dev/null +++ b/docs/zh-cn/porting.md @@ -0,0 +1,145 @@ +# 移植指南 + +FlashDB 底层的 Flash 管理及操作依赖于 RT-Thread 的 FAL (Flash Abstraction Layer) Flash 抽象层开源软件包 ,该开源库也支持运行在 **裸机平台** [(点击查看介绍)](http://packages.rt-thread.org/detail.html?package=fal)。所以只需要将所用到的 Flash 对接到 FAL ,即可完成整个移植工作。 + +## 移植介绍 + +![flashdb_porting_layer](_media/flashdb_porting_layer.png) + +移植的主要工作都在 FAL 这边,其他接口并不是强依赖,可以根据自己的情况进行对接。 + +移植前建议先了解下 FAL 功能介绍,详见:http://packages.rt-thread.org/detail.html?package=fal + +FAL 底层将不同的 Flash 存储介质进行了统一封装,并提供了分区表机制,暴露给上层用户。 + +FlashDB 的每个数据库就是基于 FAL 提供的分区机制,每个数据库都坐落在某个 FAL 的分区上,相当于一个分区对应一个数据库。 + +下面将详细讲解 FAL 的移植流程,更多移植演示可以参考已经提供的 demo 。 + +## FAL 移植 + +### 定义 flash 设备 + +在定义 Flash 设备表前,需要先定义 Flash 设备。可以是片内 flash, 也可以是片外基于 SFUD 的 spi flash: + +- 定义片内 flash 设备可以参考 [`fal_flash_stm32f2_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_stm32f2_port.c) 。 +- 定义片外 spi flash 设备可以参考 [`fal_flash_sfud_port.c`](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_flash_sfud_port.c) 。 + +定义具体的 Flash 设备对象,用户需要根据自己的 Flash 情况分别实现 `init`、 `read`、 `write`、 `erase` 这些操作函数: + +- `static int init(void)`:**可选** 的初始化操作。 +- `static int read(long offset, uint8_t *buf, size_t size)`:读取操作。 + +| 参数 | 描述 | +| ------ | ------------------------- | +| offset | 读取数据的 Flash 偏移地址 | +| buf | 存放待读取数据的缓冲区 | +| size | 待读取数据的大小 | +| return | 返回实际读取的数据大小 | + +- `static int write(long offset, const uint8_t *buf, size_t size)` :写入操作。 + +| 参数 | 描述 | +| ------ | ------------------------- | +| offset | 写入数据的 Flash 偏移地址 | +| buf | 存放待写入数据的缓冲区 | +| size | 待写入数据的大小 | +| return | 返回实际写入的数据大小 | + +- `static int erase(long offset, size_t size)` :擦除操作。 + +| 参数 | 描述 | +| ------ | ------------------------- | +| offset | 擦除区域的 Flash 偏移地址 | +| size | 擦除区域的大小 | +| return | 返回实际擦除的区域大小 | + +用户需要根据自己的 Flash 情况分别实现这些操作函数。在文件最底部定义了具体的 Flash 设备对象 ,如下示例定义了 stm32f2 片上 flash:stm32f2_onchip_flash + +```C +const struct fal_flash_dev stm32f2_onchip_flash = +{ + .name = "stm32_onchip", + .addr = 0x08000000, + .len = 1024*1024, + .blk_size = 128*1024, + .ops = {init, read, write, erase}, + .write_gran = 8 +}; +``` + +- `"stm32_onchip"` : Flash 设备的名字。 + +- `0x08000000`: 对 Flash 操作的起始地址。 + +- `1024*1024`:Flash 的总大小(1MB)。 + +- `128*1024`:Flash 块/扇区大小(因为 STM32F2 各块大小不均匀,所以擦除粒度为最大块的大小:128K)。 + +- `{init, read, write, erase}` :Flash 的操作函数。 如果没有 init 初始化过程,第一个操作函数位置可以置空。 + +- `8` : 设置写粒度,单位 bit, 0 表示未生效(默认值为 0 ),该成员是 fal 版本大于 0.4.0 的新增成员。各个 flash 写入粒度不尽相同,可通过该成员进行设置,以下列举几种常见 Flash 写粒度: + - nor flash: 1 bit + - stm32f2/f4: 8 bit + - stm32f1: 32 bit + - stm32l4: 64 bit + +### 定义 flash 设备表 + +Flash 设备表定义在 `fal_cfg.h` 头文件中,定义分区表前需 **新建 `fal_cfg.h` 文件** ,请将该文件统一放在对应 BSP 或工程目录的 port 文件夹下,并将该头文件路径加入到工程。fal_cfg.h 可以参考 [示例文件 fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_cfg.h) 完成。 + +设备表示例: + +```C +/* ===================== Flash device Configuration ========================= */ +extern const struct fal_flash_dev stm32f2_onchip_flash; +extern struct fal_flash_dev nor_flash0; + +/* flash device table */ +#define FAL_FLASH_DEV_TABLE \ +{ \ + &stm32f2_onchip_flash, \ + &nor_flash0, \ +} +``` + +Flash 设备表中,有两个 Flash 对象,一个为 STM32F2 的片内 Flash ,一个为片外的 Nor Flash。 + +### 定义 flash 分区表 + +分区表也定义在 `fal_cfg.h` 头文件中。Flash 分区基于 Flash 设备,每个 Flash 设备又可以有 N 个分区,这些分区的集合就是分区表。在配置分区表前,务必保证已定义好 **Flash 设备** 及 **设备表**。fal_cfg.h 可以参考 [示例文件 fal/samples/porting/fal_cfg.h](https://github.com/RT-Thread-packages/fal/blob/master/samples/porting/fal_cfg.h) 完成。 + +分区表示例: + +```C +#define NOR_FLASH_DEV_NAME "norflash0" +/* ====================== Partition Configuration ========================== */ +#ifdef FAL_PART_HAS_TABLE_CFG +/* partition table */ +#define FAL_PART_TABLE \ +{ \ + {FAL_PART_MAGIC_WORD, "bl", "stm32_onchip", 0, 64*1024, 0}, \ + {FAL_PART_MAGIC_WORD, "app", "stm32_onchip", 64*1024, 704*1024, 0}, \ + {FAL_PART_MAGIC_WORD, "easyflash", NOR_FLASH_DEV_NAME, 0, 1024*1024, 0}, \ + {FAL_PART_MAGIC_WORD, "download", NOR_FLASH_DEV_NAME, 1024*1024, 1024*1024, 0}, \ +} +#endif /* FAL_PART_HAS_TABLE_CFG */ +``` + +上面这个分区表详细描述信息如下: + +| 分区名 | Flash 设备名 | 偏移地址 | 大小 | 说明 | +| ----------- | -------------- | --------- | ----- | ------------------ | +| "bl" | "stm32_onchip" | 0 | 64KB | 引导程序 | +| "app" | "stm32_onchip" | 64*1024 | 704KB | 应用程序 | +| "easyflash" | "norflash0" | 0 | 1MB | EasyFlash 参数存储 | +| "download" | "norflash0" | 1024*1024 | 1MB | OTA 下载区 | + +用户需要修改的分区参数包括:分区名称、关联的 Flash 设备名、偏移地址(相对 Flash 设备内部)、大小,需要注意以下几点: + +- 分区名保证 **不能重复**; +- 关联的 Flash 设备 **务必已经在 Flash 设备表中定义好** ,并且 **名称一致** ,否则会出现无法找到 Flash 设备的错误; +- 分区的起始地址和大小 **不能超过 Flash 设备的地址范围** ,否则会导致包初始化错误; + +> 注意:每个分区定义时,除了填写上面介绍的参数属性外,需在前面增加 `FAL_PART_MAGIC_WORD` 属性,末尾增加 `0` (目前用于保留功能) + diff --git a/docs/zh-cn/quick-started.md b/docs/zh-cn/quick-started.md new file mode 100644 index 0000000..ccec088 --- /dev/null +++ b/docs/zh-cn/quick-started.md @@ -0,0 +1,63 @@ +# 快速开始 + +本文档将帮助用户快速的将 FlashDB 在演示平台上使用起来,体验 FlashDB 的实际使用效果 + +## 基本概念 + +- **键值数据库(KVDB)**:是一种非关系数据库,它将数据存储为键值(Key-Value)对集合,其中键作为唯一标识符。KVDB 操作简洁,可扩展性强。 +- **时序数据(TSDB)** :时间序列数据库 (Time Series Database , 简称 TSDB),它将数据按照 **时间顺序存储** 。TSDB 数据具有时间戳,数据存储量大,插入及查询性能高。 +- **时序记录(TSL)** :TSL (Time series log),是 TSDB 中每条记录的简称。 +- **Blob** :在计算机中,blob 常常是数据库中用来存储二进制文件的字段类型。在 FlashDB 中, KV 和 TSL 都使用 blob 类型来存储,该类型可以兼容任意变量类型。 +- **迭代器(iterator)**:它可以让用户透过特定的接口巡访容器中的每一个元素,而不用了解底层的实现。 TSDB 和 KVDB 都支持通过迭代器对数据库进行遍历访问。 + +## 功能框图 + +通过下面的功能框图,可以大致了解 FlashDB 功能模块划分 + +![flashdb_framework](_media/flashdb_framework.png) + +## 准备开发环境 + +使用前,需提前在 PC 上安装下面的开发软件 + +### 集成开发环境 + +FlashDB 提供的演示工程默认支持两种工程: + +- **RT-Studio** :免费使用,支持中文,使用方便,下载地址:https://www.rt-thread.org/page/studio.html +- **Keil MDK** :需安装 MDK v5 版本的集成开发环境 + +### 串口工具 + +准备 `串口调试助手` 或 `串口终端工具` ,后续需要在查看运行日志时使用 + +## 获取源码 + +最新代码目前托管在 GitHub 及 Gitee ,master 分支为开发版本,推荐下载已发布的版本 + +- GitHub 下载:https://github.com/armink/FlashDB/releases +- Gitee 下载:https://gitee.com/armink/FlashDB/releases + +## 选择演示平台 + +在项目的 `demos` 目录下,目前已提供下面一些硬件演示平台,可以选择一个硬件平台,真机体验一下 FlashDB 的运行过程。 + +更多详细介绍,点击下方表格中的 **使用说明** 进行查看。 + +| 硬件平台 | 路径 | flash 类型 | 使用说明 | +| --------------------- | :---------------------------- | :------------ | ----------------------------------------------- | +| stm32f10x | `demos/stm32f103ve` | stm32 on-chip | [点击查看](zh-cn/demo-stm32f103ve.md) | +| stm32f40x | `demos/stm32f405rg` | stm32 on-chip | [点击查看](zh-cn/demo-stm32f405rg.md) | +| stm32f40x + spi flash | `demos/stm32f405rg_spi_flash` | spi flash | [点击查看](zh-cn/demo-stm32f405rg-spi-flash.md) | + +## 查看示例说明 + +如果上面没有合适自己的演示平台,也可以先查看一下感兴趣的示例说明。 + +| 示例文件 | 说明 | 详解 | +| ----------------------------------- | ------------------ | ----------------------------------------- | +| `samples/kvdb_basic_sample.c` | KVDB 基础示例 | [点击查看](zh-cn/sample-kvdb-basic) | +| `samples/kvdb_type_string_sample.c` | 字符串类型 KV 示例 | [点击查看](zh-cn/sample-kvdb-type-string) | +| `samples/kvdb_type_blob_sample.c` | blob 类型 KV 示例 | [点击查看](zh-cn/sample-kvdb-type-blob) | +| `samples/tsdb_sample.c` | TSDB 基础示例 | [点击查看](zh-cn/sample-tsdb-basic) | + diff --git a/docs/zh-cn/sample-kvdb-basic.md b/docs/zh-cn/sample-kvdb-basic.md new file mode 100644 index 0000000..88affb6 --- /dev/null +++ b/docs/zh-cn/sample-kvdb-basic.md @@ -0,0 +1,60 @@ +# KVDB 基础示例 + +该示例主要演示了 KVDB 的基础功能,包括 KV 的获取及设置修改功能。 + +## 代码说明 + +示例代码位于 `samples/kvdb_basic_sample.c` ,在 `main.c` 有定义默认的 KV 集合表,在里面有 `boot_count` KV。通过该 KV 来记录当前系统的启动次数。每次掉电再启动时,该 KV 会自动加一,并保存至 KVDB 中。大致内容如下: + +```C +void kvdb_basic_sample(fdb_kvdb_t kvdb) +{ + struct fdb_blob blob; + int boot_count = 0; + + FDB_INFO("==================== kvdb_basic_sample ====================\n"); + + { /* GET the KV value */ + /* get the "boot_count" KV value */ + fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count))); + /* the blob.saved.len is more than 0 when get the value successful */ + if (blob.saved.len > 0) { + FDB_INFO("get the 'boot_count' value is %d\n", boot_count); + } else { + FDB_INFO("get the 'boot_count' failed\n"); + } + } + + { /* CHANGE the KV value */ + /* increase the boot count */ + boot_count ++; + /* change the "boot_count" KV's value */ + fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count))); + FDB_INFO("set the 'boot_count' value to %d\n", boot_count); + } + + FDB_INFO("===========================================================\n"); +} +``` + +## 首次运行日志 + +当前 `boot_count` 为 0 ,对其加一后,存入数据库。 + +``` +[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ==================== +[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 0 +[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 1 +[FlashDB][sample][kvdb][basic] =========================================================== +``` + +## 二次运行日志 + +当前 `boot_count` 为 1 ,说明上次的保存生效了,再对其加一后保存,供下次访问使用。 + +``` +[FlashDB][sample][kvdb][basic] ==================== kvdb_basic_sample ==================== +[FlashDB][sample][kvdb][basic] get the 'boot_count' value is 1 +[FlashDB][sample][kvdb][basic] set the 'boot_count' value to 2 +[FlashDB][sample][kvdb][basic] =========================================================== +``` \ No newline at end of file diff --git a/docs/zh-cn/sample-kvdb-traversal.md b/docs/zh-cn/sample-kvdb-traversal.md new file mode 100644 index 0000000..def2dbc --- /dev/null +++ b/docs/zh-cn/sample-kvdb-traversal.md @@ -0,0 +1,38 @@ +# 遍历所有 KV + +本示例演示了如果遍历 KVDB 中的所有 KV ,用户可以在遍历 KV 时增加自己的处理动作。 + +## 代码说明 + +下面的示例代码中,首先初始化了 KVDB 的迭代器,然后使用迭代器 API ,将 KVDB 的所有 KV 逐一遍历出来。 + +遍历出来的 KV 对象含有 KV 的一些属性,包括:key name, value saved addr, value length 等,用户通过 `fdb_blob_read` 配合 `fdb_kv_to_blob` 读取出来,做一些自己的业务处理。 + +```C +void kvdb_tarversal_sample(fdb_kvdb_t kvdb) +{ + struct fdb_kv_iterator iterator; + fdb_kv_t cur_kv; + struct fdb_blob blob; + size_t data_size; + uint8_t *data_buf; + + fdb_kv_iterator_init(&iterator); + + while (fdb_kv_iterate(kvdb, &iterator)) { + cur_kv = &(iterator.curr_kv); + data_size = (size_t) cur_kv->value_len; + data_buf = (uint8_t *) malloc(data_size); + if (data_buf == NULL) { + FDB_INFO("Error: malloc failed.\n"); + break; + } + fdb_blob_read((fdb_db_t) kvdb, fdb_kv_to_blob(cur_kv, fdb_blob_make(&blob, data_buf, data_size))); + /* + * balabala do what ever you like with blob... + */ + free(data_buf); + } +} +``` + diff --git a/docs/zh-cn/sample-kvdb-type-blob.md b/docs/zh-cn/sample-kvdb-type-blob.md new file mode 100644 index 0000000..5d91b1b --- /dev/null +++ b/docs/zh-cn/sample-kvdb-type-blob.md @@ -0,0 +1,71 @@ +# blob 类型 KV 示例 + +该示例主要演示了blob KV 的相关功能,blob KV 是一个比较常用类型,其 value 是一个没有类型限制的二进制类型。在功能上,blob KV 也兼容字符串 KV 。在 API 的使用上, blob KV 拥有一套独立的 API ,可以很快速的实现各种类型 KV 到 KVDB 中的存储,比如:基本类型,数组以及结构体等。 + +## 代码说明 + +示例代码位于 `samples/kvdb_type_blob.c` ,使用一个名为 `"temp"` 的 KV 来存储温度值,分别演示了 blob KV 从 `创建->读取->修改->删除` 的全过程。大致内容如下: + +```C +void kvdb_type_blob_sample(fdb_kvdb_t kvdb) +{ + struct fdb_blob blob; + + FDB_INFO("==================== kvdb_type_blob_sample ====================\n"); + + { /* CREATE new Key-Value */ + int temp_data = 36; + + /* It will create new KV node when "temp" KV not in database. + * fdb_blob_make: It's a blob make function, and it will return the blob when make finish. + */ + fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); + FDB_INFO("create the 'temp' blob KV, value is: %d\n", temp_data); + } + + { /* GET the KV value */ + int temp_data = 0; + + /* get the "temp" KV value */ + fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); + /* the blob.saved.len is more than 0 when get the value successful */ + if (blob.saved.len > 0) { + FDB_INFO("get the 'temp' value is: %d\n", temp_data); + } + } + + { /* CHANGE the KV value */ + int temp_data = 38; + + /* change the "temp" KV's value to 38 */ + fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); + FDB_INFO("set 'temp' value to %d\n", temp_data); + } + + { /* DELETE the KV by name */ + fdb_kv_del(kvdb, "temp"); + FDB_INFO("delete the 'temp' finish\n"); + } + + FDB_INFO("===========================================================\n"); +} +``` + +## 运行日志 + +通过日志可以看出: + +- 首先创建了一个 KV 名为 `"temp"` ,并给予初值 36℃ +- 读取 `"temp"` KV 当前的值,发现与初值相同 +- 修改 `"temp"` KV 的值为 38℃ +- 最后删除 `"temp"` KV + +``` +[FlashDB][sample][kvdb][blob] ==================== kvdb_type_blob_sample ==================== +[FlashDB][sample][kvdb][blob] create the 'temp' blob KV, value is: 36 +[FlashDB][sample][kvdb][blob] get the 'temp' value is: 36 +[FlashDB][sample][kvdb][blob] set 'temp' value to 38 +[FlashDB][sample][kvdb][blob] delete the 'temp' finish +[FlashDB][sample][kvdb][blob] =========================================================== +``` + diff --git a/docs/zh-cn/sample-kvdb-type-string.md b/docs/zh-cn/sample-kvdb-type-string.md new file mode 100644 index 0000000..61ea2b0 --- /dev/null +++ b/docs/zh-cn/sample-kvdb-type-string.md @@ -0,0 +1,69 @@ +# 字符串类型 KV 示例 + +该示例主要演示了字符串 KV 的相关功能,字符串 KV 作为一个特殊的 KV 类型,其 Key 与 Value 均为字符串,常被用于参数存储、命令存储等可读性要求较高的场景。 + +## 代码说明 + +示例代码位于 `samples/kvdb_type_string.c` ,使用一个名为 `"temp"` 的 KV 来存储温度值,分别演示了字符串 KV 从 `创建->读取->修改->删除` 的全过程。大致内容如下: + +```C +void kvdb_type_string_sample(fdb_kvdb_t kvdb) +{ + FDB_INFO("==================== kvdb_type_string_sample ====================\n"); + { /* CREATE new Key-Value */ + char temp_data[10] = "36C"; + + /* It will create new KV node when "temp" KV not in database. */ + fdb_kv_set(kvdb, "temp", temp_data); + FDB_INFO("create the 'temp' string KV, value is: %s\n", temp_data); + } + + { /* GET the KV value */ + char *return_value, temp_data[10] = { 0 }; + + /* Get the "temp" KV value. + * NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible. + */ + return_value = fdb_kv_get(kvdb, "temp"); + /* the return value is NULL when get the value failed */ + if (return_value != NULL) { + strncpy(temp_data, return_value, sizeof(temp_data)); + FDB_INFO("get the 'temp' value is: %s\n", temp_data); + } + } + + { /* CHANGE the KV value */ + char temp_data[10] = "38C"; + + /* change the "temp" KV's value to "38.1" */ + fdb_kv_set(kvdb, "temp", temp_data); + FDB_INFO("set 'temp' value to %s\n", temp_data); + } + + { /* DELETE the KV by name */ + fdb_kv_del(kvdb, "temp"); + FDB_INFO("delete the 'temp' finish\n"); + } + + FDB_INFO("===========================================================\n"); +} +``` + +## 运行日志 + +通过日志可以看出: + +- 首先创建了一个 KV 名为 `"temp"` ,并给予初值 36℃ +- 读取 `"temp"` KV 当前的值,发现与初值相同 +- 修改 `"temp"` KV 的值为 38℃ +- 最后删除 `"temp"` KV + +``` +[FlashDB][sample][kvdb][string] ==================== kvdb_type_string_sample ==================== +[FlashDB][sample][kvdb][string] create the 'temp' string KV, value is: 36C +[FlashDB][sample][kvdb][string] get the 'temp' value is: 36C +[FlashDB][sample][kvdb][string] set 'temp' value to 38C +[FlashDB][sample][kvdb][string] delete the 'temp' finish +[FlashDB][sample][kvdb][string] =========================================================== +``` + diff --git a/docs/zh-cn/sample-tsdb-basic.md b/docs/zh-cn/sample-tsdb-basic.md new file mode 100644 index 0000000..1196cf9 --- /dev/null +++ b/docs/zh-cn/sample-tsdb-basic.md @@ -0,0 +1,169 @@ +# TSDB 基础示例 + +该示例主要演示了 TSDB 的基础功能,包括 TSL(时序记录)的追加、查询及状态修改功能。 + +## 代码说明 + +示例代码位于 `samples/tsdb_sample.c` ,包含追加、查询及状态修改这几个过程,大致代码如下: + +```C +void tsdb_sample(fdb_tsdb_t tsdb) +{ + struct fdb_blob blob; + + FDB_INFO("==================== tsdb_sample ====================\n"); + + { /* APPEND new TSL (time series log) */ + struct env_status status; + + /* append new log to TSDB */ + status.temp = 36; + status.humi = 85; + fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status))); + FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi); + + status.temp = 38; + status.humi = 90; + fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status))); + FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi); + } + + { /* QUERY the TSDB */ + /* query all TSL in TSDB by iterator */ + fdb_tsl_iter(tsdb, query_cb, tsdb); + } + + { /* QUERY the TSDB by time */ + /* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */ + struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }; + struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }; + time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to); + size_t count; + /* query all TSL in TSDB by time */ + fdb_tsl_iter_by_time(tsdb, from_time, to_time, query_by_time_cb, tsdb); + /* query all FDB_TSL_WRITE status TSL's count in TSDB by time */ + count = fdb_tsl_query_count(tsdb, from_time, to_time, FDB_TSL_WRITE); + FDB_INFO("query count is: %u\n", count); + } + + { /* SET the TSL status */ + /* Change the TSL status by iterator or time iterator + * set_status_cb: the change operation will in this callback + * + * NOTE: The actions to modify the state must be in orderC. + * like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2 + * The intermediate states can also be ignored. + * such as: FDB_TSL_WRITE -> FDB_TSL_DELETED + */ + fdb_tsl_iter(tsdb, set_status_cb, tsdb); + } + + FDB_INFO("===========================================================\n"); +} +``` + +分别来看下这几个过程 + +- **追加**:分两次修改结构体对象 `status` 的值然后追加到 TSDB 中; + +- **查询**:通过 TSDB 的迭代器 API ,在每次迭代时会自动执行 `query_cb` 回调函数,实现对 TSDB 中所有记录的查询,回调函数内容如下: + + ```C + static bool query_cb(fdb_tsl_t tsl, void *arg) + { + struct fdb_blob blob; + struct env_status status; + fdb_tsdb_t db = arg; + + fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status)))); + FDB_INFO("[query_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi); + + return false; + } + ``` + +- **按时间查询**:TSDB 还提供了按时间迭代的 API : `fdb_tsl_iter_by_time` ,可以传入起始和截至时间,此时迭代器会按照传入的时间段,对时序记录进行迭代。每次迭代时会执行 `query_by_time_cb` 回调,在回调中读取当前记录的内容,并打印出来。 + + ```C + static bool query_by_time_cb(fdb_tsl_t tsl, void *arg) + { + struct fdb_blob blob; + struct env_status status; + fdb_tsdb_t db = arg; + + fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status)))); + FDB_INFO("[query_by_time_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi); + + return false; + } + ``` + +- **修改状态**:每条 TSL 在被追加到 TSDB 后,都可以修改其状态,状态共有 4 种: + + - `FDB_TSL_WRITE`:已写入状态,TSL 被追加到 TSDB 中后的默认状态; + + - `FDB_TSL_USER_STATUS1 `:该状态介于写入与删除之间,用户可自定义其状态含义,比如:数据已被同步至云端; + + - `FDB_TSL_DELETED` :已删除状态,当 TSL 需要删除时,修改 TSL 的状态为该状态即可; + + > 提示:在 FlashDB 中,为了提升 Flash 寿命,删除动作并不会真正的将数据从 Flash 从擦除,而是将其标记为删除状态,用户可以通过状态对不同的数据记录进行区分。 + + - `FDB_TSL_USER_STATUS2`:删除状态之后的自定义状态,预留给用户使用; + + 修改状态时只能按照 `FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2` 顺序进行修改,不能逆序修改。也可以跳过中间状态,例如:从 `FDB_TSL_WRITE` 直接修改为`FDB_TSL_DELETED` 状态,跳过 `FDB_TSL_USER_STATUS1` 状态。 + + 示例中通过迭代器将当前所有的 TSL 都修改为 `FDB_TSL_USER_STATUS1` 状态,迭代器中的回调代码如下: + + ```C + static bool set_status_cb(fdb_tsl_t tsl, void *arg) + { + fdb_tsdb_t db = arg; + + FDB_INFO("set the TSL (time %ld) status from %d to %d\n", tsl->time, tsl->status, FDB_TSL_USER_STATUS1); + fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1); + + return false; + } + ``` + +## 首次运行日志 + +通过日志可看出,示例首先追加了两条 TSL ,每条 TSL 分别存放了不同的温度及湿度记录。然后再通过普通查询和按时间查询方式,将 TSDB 中的 TSL 获取出来,最后修改其状态,从 `2: FDB_TSL_WRITE` 到 `3: FDB_TSL_USER_STATUS1` 。 + +``` +[FlashDB][sample][tsdb] ==================== tsdb_sample ==================== +[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85) +[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90) +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] query count is: 2 +[FlashDB][sample][tsdb] set the TSL (time 1) status from 2 to 3 +[FlashDB][sample][tsdb] set the TSL (time 2) status from 2 to 3 +[FlashDB][sample][tsdb] =========================================================== +``` + +## 二次运行日志 + +第二次运行时,依旧会追加两条 TSL 。再看查询的结果,共计 4 条,包含了 2 条首次运行时追加的 TSL。通过打印的 time 时间戳可以看出,模拟时间戳工作正常。`query count is: 2` 说明了 TSDB 虽然有 4 条记录,但只有 2 条记录是写入状态。 + +``` +[FlashDB][sample][tsdb] ==================== tsdb_sample ==================== +[FlashDB][sample][tsdb] append the new status.temp (36) and status.humi (85) +[FlashDB][sample][tsdb] append the new status.temp (38) and status.humi (90) +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 3, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_cb] queried a TSL: time: 4, temp: 38, humi: 90 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 1, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 2, temp: 38, humi: 90 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 3, temp: 36, humi: 85 +[FlashDB][sample][tsdb] [query_by_time_cb] queried a TSL: time: 4, temp: 38, humi: 90 +[FlashDB][sample][tsdb] query count is: 2 +[FlashDB][sample][tsdb] set the TSL (time 1) status from 3 to 3 +[FlashDB][sample][tsdb] set the TSL (time 2) status from 3 to 3 +[FlashDB][sample][tsdb] set the TSL (time 3) status from 2 to 3 +[FlashDB][sample][tsdb] set the TSL (time 4) status from 2 to 3 +[FlashDB][sample][tsdb] =========================================================== +``` \ No newline at end of file diff --git a/docs/zh/api.md b/docs/zh/api.md deleted file mode 100644 index 52f4a86..0000000 --- a/docs/zh/api.md +++ /dev/null @@ -1,5 +0,0 @@ -# FlashDB API 说明 - ---- - -马上就来…… \ No newline at end of file diff --git a/docs/zh/design.md b/docs/zh/design.md deleted file mode 100644 index 8c781f6..0000000 --- a/docs/zh/design.md +++ /dev/null @@ -1,3 +0,0 @@ -# FlashDB 功能设计与实现 - -马上就来…… \ No newline at end of file diff --git a/docs/zh/port.md b/docs/zh/port.md deleted file mode 100644 index 1ab6040..0000000 --- a/docs/zh/port.md +++ /dev/null @@ -1,5 +0,0 @@ -# FlashDB 移植说明 - ---- - -马上就来…… \ No newline at end of file diff --git a/docs/zh/readme.md b/docs/zh/readme.md deleted file mode 100644 index bec6d45..0000000 --- a/docs/zh/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -|文件名 |描述| -|:----- |:----| -|api.md |API 说明| -|port.md |移植说明| -|design.md |设计文档| \ No newline at end of file