diff options
-rw-r--r-- | ext/wasm/GNUmakefile | 38 | ||||
-rw-r--r-- | ext/wasm/fiddle.make | 3 | ||||
-rw-r--r-- | ext/wasm/mkwasmbuilds.c | 74 | ||||
-rw-r--r-- | ext/wasm/wasmfs.make | 5 | ||||
-rw-r--r-- | manifest | 20 | ||||
-rw-r--r-- | manifest.uuid | 2 |
6 files changed, 73 insertions, 69 deletions
diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index 87c2ca2fc..5cd0aa66a 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -457,6 +457,21 @@ else emcc_opt ?= -Oz endif +# Our JS code installs bindings of each sqlite3_...() WASM export. The +# generated Emscripten JS file does the same using its own framework, +# but we don't use those results and can speed up lib init, and reduce +# memory cost a bit, by stripping them out. Emscripten-side changes +# can "break" this, causing this to be a no-op, but the worst that can +# happen in that case is that it doesn't actually strip anything, +# leading to slightly larger JS files. +# +# This snippet is intended to be used in makefile targets which +# generate an Emscripten module and where $@ is the module's .js/.mjs +# file. +SQLITE.strip-createExportWrapper = \ + sed -i -e '/^.*= \(_sqlite3\|_fiddle\)[^=]* = createExportWrapper/d' $@ || exit; \ + echo '(Probably) stripped out extraneous createExportWrapper() parts.' + # When passing emcc_opt from the CLI, += and re-assignment have no # effect, so emcc_opt+=-g3 doesn't work. So... emcc_opt_full = $(emcc_opt) -g3 @@ -598,7 +613,7 @@ emcc.cflags = emcc.cflags += -std=c99 -fPIC # -------------^^^^^^^^ we need c99 for $(sqlite3-wasm.c), primarily # for variadic macros and snprintf() to implement -# sqlite3_wasm_enum_json(). +# sqlite3__wasm_enum_json(). emcc.cflags += -I. -I$(dir.top) ######################################################################## # emcc flags specific to building .js/.wasm files... @@ -943,23 +958,24 @@ $(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1-promiser.c-pp.j $(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\ $(dir.dout)/sqlite3-worker1-promiser.mjs,\ -Dtarget=es6-module -Dtarget=es6-bundler-friendly)) -$(dir.dout)/sqlite3-bundler-friendly.mjs: $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs \ - $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js $(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.js)) $(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.mjs,\ -Dtarget=es6-module)) $(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser.html)) $(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser-esm.html,\ -Dtarget=es6-module)) -all: $(dir.dout)/sqlite3-worker1.js \ - $(dir.dout)/sqlite3-worker1-promiser.js $(dir.dout)/sqlite3-worker1-promiser.mjs + +$(dir.dout)/sqlite3-bundler-friendly.mjs: \ + $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs \ + $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js demo-worker1-promiser.html: $(dir.dout)/sqlite3-worker1-promiser.js demo-worker1-promiser.js demo-worker1-promiser-esm.html: $(sqlite3-worker1-promiser.mjs) demo-worker1-promiser.mjs all: demo-worker1-promiser.html demo-worker1-promiser-esm.html sqlite3-api.ext.jses += \ - $(sqlite3-worker1-promiser.mjs) \ + $(dir.dout)/sqlite3-worker1-promiser.mjs \ + $(dir.dout)/sqlite3-worker1-promiser.js \ $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs \ $(dir.dout)/sqlite3-worker1.js all quick: $(sqlite3-api.ext.jses) @@ -1039,7 +1055,6 @@ $(EXPORTED_FUNCTIONS.speedtest1): $(MKDIR.bld) $(EXPORTED_FUNCTIONS.api.core) @echo "Making $@ ..." @{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.core); } > $@ speedtest1.js = $(dir.dout)/speedtest1.js -speedtest1.wasm = $(dir.dout)/speedtest1.wasm emcc.flags.speedtest1-vanilla = $(cflags.common) -DSQLITE_SPEEDTEST1_WASM speedtest1.cfiles = $(speedtest1.c) $(sqlite3-wasm.c) $(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \ @@ -1055,14 +1070,13 @@ $(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \ -USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c) \ $(speedtest1.exit-runtime0) \ -o $@ $(speedtest1.cfiles) -lm - $(maybe-wasm-strip) $(speedtest1.wasm) - sed -i -e '/^var _sqlite3.*createExportWrapper/d' $@ - chmod -x $(speedtest1.wasm) - ls -la $@ $(speedtest1.wasm) + @chmod -x $(basename $@).wasm + @$(maybe-wasm-strip) $(basename $@).wasm + @$(SQLITE.strip-createExportWrapper) + @ls -la $@ $(speedtest1.wasm) speedtest1: $(speedtest1.js) all: speedtest1 -#CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm) # end speedtest1.js ######################################################################## diff --git a/ext/wasm/fiddle.make b/ext/wasm/fiddle.make index 0cd6b4d36..6bdf44195 100644 --- a/ext/wasm/fiddle.make +++ b/ext/wasm/fiddle.make @@ -67,9 +67,6 @@ $(EXPORTED_FUNCTIONS.fiddle): $(MKDIR.bld) $(fiddle.EXPORTED_FUNCTIONS.in) \ fiddle.cses = $(dir.top)/shell.c $(sqlite3-wasm.c) -fiddle: $(fiddle-module.js) $(fiddle-module.js.debug) -fiddle.debug: $(fiddle-module.js.debug) - clean: clean-fiddle clean-fiddle: rm -f $(dir.fiddle)/fiddle-module.js \ diff --git a/ext/wasm/mkwasmbuilds.c b/ext/wasm/mkwasmbuilds.c index d70de042e..d33a10c01 100644 --- a/ext/wasm/mkwasmbuilds.c +++ b/ext/wasm/mkwasmbuilds.c @@ -59,14 +59,14 @@ static const char * zBanner = ** to breakage in some of the flag checks. */ enum LibModeFlags { - /* Sentinel value */ - LIBMODE_PLAIN = 0, /* Indicates an ESM module build. */ LIBMODE_ESM = 0x01, /* Indicates a "bundler-friendly" build mode. */ LIBMODE_BUNDLER_FRIENDLY = 0x02, - /* Indicates to _not_ add this build to the 'all' target. */ - LIBMODE_DONT_ADD_TO_ALL = 0x04, + /* Indicates that this build is unsupported. Such builds are not + ** added to the 'all' target. The unsupported builds exist primarily + ** for experimentation's sake. */ + LIBMODE_UNSUPPORTED = 0x04, /* Indicates a node.js-for-node.js build (untested and ** unsupported). */ LIBMODE_NODEJS = 0x08, @@ -101,29 +101,30 @@ typedef struct BuildDef BuildDef; */ const BuildDef aBuildDefs[] = { {/* Core build */ - "sqlite3", "vanilla", LIBMODE_PLAIN, "$(sqlite3.js)", 0, 0}, + "sqlite3", "vanilla", 0, "$(sqlite3.js)", 0, 0}, {/* Core ESM */ "sqlite3", "esm", LIBMODE_ESM, "$(sqlite3.mjs)", "-Dtarget=es6-module", 0}, - {/* Core bundler-friend. Untested and "not really" supported, but - ** required by the downstream npm subproject. */ + {/* Core bundler-friendly build. Untested and "not really" + ** supported, but required by the downstream npm subproject. + ** Testing these would require special-purpose node-based tools and + ** custom test apps. Or we can pass them off as-is to the npm + ** subproject and they spot failures pretty quickly ;). */ "sqlite3", "bundler-friendly", LIBMODE_BUNDLER_FRIENDLY | LIBMODE_ESM, "$(dir.dout)/sqlite3-bundler-friendly.mjs", "$(c-pp.D.sqlite3-esm) -Dtarget=es6-bundler-friendly", 0}, {/* node.js mode. Untested and unsupported. */ - "sqlite3", "node", LIBMODE_NODEJS | LIBMODE_DONT_ADD_TO_ALL, + "sqlite3", "node", LIBMODE_UNSUPPORTED | LIBMODE_NODEJS, "$(dir.dout)/sqlite3-node.mjs", "$(c-pp.D.sqlite3-bundler-friendly) -Dtarget=node", 0}, - {/* The wasmfs build is optional, untested, unsupported, and - ** needs to be invoked conditionally using info we don't have - ** here. */ + {/* Wasmfs build. Fully unsupported and largely untested. */ "sqlite3-wasmfs", "esm" , - LIBMODE_WASMFS | LIBMODE_ESM | LIBMODE_DONT_ADD_TO_ALL, + LIBMODE_UNSUPPORTED | LIBMODE_WASMFS | LIBMODE_ESM, "$(dir.wasmfs)/sqlite3-wasmfs.mjs", "$(c-pp.D.sqlite3-bundler-friendly) -Dwasmfs", "-sEXPORT_ES6 -sUSE_ES6_IMPORT_META"}, @@ -327,8 +328,6 @@ static void mk_fiddle(void){ pf("%s# Begin fiddle%s\n", zBanner, zTail); pf("fiddle-module.js%s = %s/fiddle-module.js\n", zTail, zDir); - pf("fiddle-module.wasm%s = " - "$(subst .js,.wasm,$(fiddle-module.js%s))\n", zTail, zTail); pf("$(fiddle-module.js%s):%s $(MAKEFILE_LIST) $(MAKEFILE.fiddle) " "$(EXPORTED_FUNCTIONS.fiddle) " "$(fiddle.cses) $(pre-post-fiddle-module-vanilla.deps) " @@ -340,7 +339,9 @@ static void mk_fiddle(void){ pf("\t$(bin.emcc) -o $@ $(fiddle.emcc-flags%s) " "$(pre-post-fiddle-module-vanilla.flags) $(fiddle.cses)\n", zTail); - pf("\t$(maybe-wasm-strip) $(fiddle-module.wasm%s)\n", zTail); + ps("\t@chmod -x $(basename $@).wasm"); + ps("\t@$(maybe-wasm-strip) $(basename $@).wasm"); + ps("\t@$(SQLITE.strip-createExportWrapper)"); pf("\t@cp -p $(SOAP.js) $(dir $@)\n"); if( 1==i ){/*fiddle.debug*/ pf("\tcp -p $(dir.fiddle)/index.html " @@ -349,13 +350,13 @@ static void mk_fiddle(void){ "$(dir $@)\n"); } pf("\t@for i in %s/*.*js %s/*.html %s/*.wasm; do \\\n" - "\t\ttest -f $${i} || continue; \\\n" + "\t\ttest -f $${i} || continue; \\\n" "\t\tgzip < $${i} > $${i}.gz; \\\n" "\tdone\n", zDir, zDir, zDir); if( 0==i ){ ps("fiddle: $(fiddle-module.js)"); }else{ - ps("fiddle-debug: $(fiddle-module-debug.js)"); + ps("fiddle-debug: $(fiddle-module.js.debug)"); } pf("# End fiddle%s%s", zTail, zBanner); } @@ -392,13 +393,17 @@ static void mk_lib_mode(const BuildDef * pB){ "$(pre-post-%s-%s.deps) " "$(sqlite3-api.ext.jses)" /* ^^^ maintenance reminder: we set these as deps so that they - get copied into place early. That allows the developer to - reload the base-most test pages while the later-stage builds - are still compiling, which is especially helpful when running - builds with long build times (like -Oz). */ + ** get copied into place early. That allows the developer to + ** reload the base-most test pages while the later-stage builds + ** are still compiling, which is especially helpful when running + ** builds with long build times (like -Oz). */ "\n", pB->zJsOut, zNM); pf("\t@echo \"Building $@ ...\"\n"); + if( LIBMODE_UNSUPPORTED & pB->flags ){ + ps("\t@echo 'ACHTUNG: $@ is an unsupported build. " + "Use at your own risk.'"); + } pf("\t$(bin.emcc) -o $@ $(emcc_opt_full) $(emcc.flags) \\\n"); pf("\t\t$(emcc.jsflags) -sENVIRONMENT=$(emcc.environment.%s) \\\n", pB->zMode); @@ -415,28 +420,19 @@ static void mk_lib_mode(const BuildDef * pB){ pf("\t@$(call SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT,1,%d)\n", (LIBMODE_WASMFS & pB->flags) ? 1 : 0); } - pf("\t@chmod -x %s; \\\n" - "\t\t$(maybe-wasm-strip) %s;\n", - zWasmOut, zWasmOut); + pf("\t@chmod -x %s\n", zWasmOut); + pf("\t@$(maybe-wasm-strip) %s\n", zWasmOut); pf("\t@$(call SQLITE.CALL.WASM-OPT,%s)\n", zWasmOut); - pf("\t@sed -i -e '/^.*= *_sqlite.*= *createExportWrapper/d' %s || exit; \\\n" - /* ^^^^^^ reminder: Mac/BSD sed has no -i flag */ - "\t\techo 'Stripped out createExportWrapper() parts.'\n", - pB->zJsOut) /* Our JS code installs bindings of each WASM export. The - generated Emscripten JS file does the same using its - own framework, but we don't use those results and can - speed up lib init, and reduce memory cost - considerably, by stripping them out. */; + ps("\t@$(SQLITE.strip-createExportWrapper)"); /* - ** The above $(bin.emcc) call will write zJsOut and will create a - ** like-named .wasm file (zWasmOut). That .wasm file name gets - ** hard-coded into zJsOut so we need to, for some cases, patch - ** zJsOut to use the name sqlite3.wasm instead. Note that the + ** The above $(bin.emcc) call will write pB->zJsOut, a.k.a. $@, and + ** will create a like-named .wasm file (zWasmOut). That .wasm file + ** name gets hard-coded into $@ so we need to, for some cases, patch + ** zJsOut to use the name sqlite3.wasm instead. Note that the ** resulting .wasm file is identical for all builds for which zEmcc ** is empty. */ - if( (LIBMODE_BUNDLER_FRIENDLY & pB->flags) - || (LIBMODE_NODEJS & pB->flags) ){ + if( (LIBMODE_BUNDLER_FRIENDLY & pB->flags) ){ pf("\t@echo 'Patching $@ for %s.wasm...'; \\\n", pB->zName); pf("\t\trm -f %s; \\\n", zWasmOut); pf("\t\tsed -i -e 's/%s-%s.wasm/%s.wasm/g' $@ || exit;\n", @@ -457,7 +453,7 @@ static void mk_lib_mode(const BuildDef * pB){ }else{ pf("\t@ls -la %s $@\n", zWasmOut); } - if( 0==(LIBMODE_DONT_ADD_TO_ALL & pB->flags) ){ + if( 0==(LIBMODE_UNSUPPORTED & pB->flags) ){ pf("all: %s\n", pB->zJsOut); } pf("# End build [%s-%s]%s", zNM, zBanner); diff --git a/ext/wasm/wasmfs.make b/ext/wasm/wasmfs.make index 712418fa4..0d1fb4043 100644 --- a/ext/wasm/wasmfs.make +++ b/ext/wasm/wasmfs.make @@ -10,10 +10,7 @@ MAKEFILE.wasmfs = $(lastword $(MAKEFILE_LIST)) # $(warning) alignment! ifneq (1,$(MAKING_CLEAN)) $(warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!) - $(warning !! The WASMFS build is not well-supported. WASMFS is a proverbial) - $(warning !! moving target, sometimes changing in incompatible ways between) - $(warning !! Emscripten versions. This build is provided for adventurous folks) - $(warning !! and is not a supported deliverable of the SQLite project.) + $(warning !! The WASMFS build is unsupported. Use at your own risk. $(warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!) endif @@ -1,5 +1,5 @@ -C Fix\svtabH.test\sso\sthat\sit\sworks\son\swindows\seven\sif\sthere\sare\sfiles\sthat\sbegin\swith\s"$"\sin\sthe\sroot\sdirectory. -D 2025-07-16T17:24:31.516 +C Diverse\swasm\sbuild\scleanups. +D 2025-07-16T20:50:40.841 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -622,7 +622,7 @@ F ext/session/sqlite3session.c 19e14bcca2fbc63a8022ffd708ea6e6986c4003a1e9bbca9b F ext/session/sqlite3session.h b81e8536ce4b83babafd700f4ff67017804b6c1d71df963b30d3972958e7f4a7 F ext/session/test_session.c 8766b5973a6323934cb51248f621c3dc87ad2a98f023c3cc280d79e7d78d36fb F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c -F ext/wasm/GNUmakefile b9217715b615bfe51a8c13f6fa9889fb7b734e0f26bffc656ff52652e9906cc1 +F ext/wasm/GNUmakefile 35e730a01b32481f5483ea5bd72c3d4609e25f34cb5aab9f85eb3eba6f0c4935 F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a F ext/wasm/README.md b89605f65661cf35bf034ff6d43e448cc169b8017fc105d498e33b81218b482c F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff @@ -675,7 +675,7 @@ F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2 F ext/wasm/demo-worker1.js 08720227e98fa5b44761cf6e219269cee3e9dd0421d8d91459535da776950314 F ext/wasm/dist.make c29018b4db479a4c170569393e5399f0625446123a7eb6ffb0677495292bb954 F ext/wasm/example_extra_init.c 2347cd69d19d839ef4e5e77b7855103a7fe3ef2af86f2e8c95839afd8b05862f -F ext/wasm/fiddle.make 88885c5a2102e1516d0fb89e0cdb023e5b5d73baff9a8ebf97a657f6fb63668b +F ext/wasm/fiddle.make ea505d11aa2a89551e1693ed4c71ee6a163364ca14f806dda295d0beb26ec0ea F ext/wasm/fiddle/fiddle-worker.js 850e66fce39b89d59e161d1abac43a181a4caa89ddeea162765d660277cd84ce F ext/wasm/fiddle/fiddle.js 2a2f27b4be2674f501fff61c4a09e44dcf2295731a26b5c28e439f3a573bd269 F ext/wasm/fiddle/index.html 7fcfb221165183bef0e05d5af9ceb79b527e799b1708ab05de0ec0eaebd5b7bf @@ -683,7 +683,7 @@ F ext/wasm/index-dist.html 56132399702b15d70c474c3f1952541e25cb0922942868f70daf1 F ext/wasm/index.html bcaa00eca521b372a6a62c7e7b17a870b0fcdf3e418a5921df1fd61e5344080d F ext/wasm/jaccwabyt/jaccwabyt.js 6e4f26d0edb5c2e7d381b7eff1924832a040a12274afab2d1e1789027e9f6c5c F ext/wasm/jaccwabyt/jaccwabyt.md 1128e3563e7eff90b5a373395251fc76cb32386fad1fea6075b0f34a8f1b9bdf -F ext/wasm/mkwasmbuilds.c f5c143c10aeb7a519f7c08f98f90927f43c1991af3373bc6f80d8631a58acd42 +F ext/wasm/mkwasmbuilds.c cc66cfaf8673ece3c30ca7fe28f6111481090648098a143ea619a8820b8fbe82 F ext/wasm/module-symbols.html dc476b403369b26a1a23773e13b80f41b9a49f0825e81435fe3600a7cfbbe337 F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96 F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63 @@ -708,7 +708,7 @@ F ext/wasm/tests/opfs/sahpool/digest.html 206d08a34dc8bd570b2581d3d9ab3ecad3201b F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca01385e2732294b53f4c842328 F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2 F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61 -F ext/wasm/wasmfs.make bde552956ae47f21c3eb1bb727e5fd416a6df19758f96b77ba72b20612359b8a +F ext/wasm/wasmfs.make 411dd94b40406572caddf88392a1ccc4deed0f88d260516e59ca6e0c887ee861 F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0 F main.mk a5d698cf8d38e4eb42a89f08a9521906b24f4acac61bbafa56d72cd9b39617b6 F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 @@ -2213,8 +2213,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 7ef22c3d11088210d2267375ec188bd352b067614200394b9877f2e40dc12bb2 -R 3ef038ae9187f207008c5c09bc7a42a8 -U dan -Z 562bf6bcb2e06809b99eb8ab4d03b298 +P 19a79219a7d52272102ff09d19e6b9b87e88e0070592fd7e6040bd8ce66ad238 +R 30226b4a55673400a1aa3c71febc4b89 +U stephan +Z 7a7ad60cbd528275276e18c6ff4c005a # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a03432876..f1c904a3d 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -19a79219a7d52272102ff09d19e6b9b87e88e0070592fd7e6040bd8ce66ad238 +14ca18f72a7edde7c65a6c6b23fdc3f5ed6860371795926045c987f7b2c75e1b |